├── .github └── dependabot.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── NOTICE ├── etc ├── license-header.txt └── notice-template.txt ├── pom.xml └── src ├── main ├── java-templates │ └── org │ │ └── n52 │ │ └── jackson │ │ └── datatype │ │ └── jts │ │ └── VersionInfo.java ├── java │ └── org │ │ └── n52 │ │ └── jackson │ │ └── datatype │ │ └── jts │ │ ├── Field.java │ │ ├── GeometryDeserializer.java │ │ ├── GeometrySerializer.java │ │ ├── GeometryType.java │ │ ├── IncludeBoundingBox.java │ │ ├── JtsModule.java │ │ ├── Type.java │ │ └── TypeSafeJsonDeserializer.java └── resources │ └── META-INF │ └── services │ └── com.fasterxml.jackson.databind.Module └── test └── java └── org └── n52 └── jackson └── datatype └── jts └── DeserializeTest.java /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/5be64312ae8e6afe2b62b550400041e90c69db6a/Global/JetBrains.gitignore 2 | 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/usage.statistics.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | 13 | # Generated files 14 | .idea/**/contentModel.xml 15 | 16 | # Sensitive or high-churn files 17 | .idea/**/dataSources/ 18 | .idea/**/dataSources.ids 19 | .idea/**/dataSources.local.xml 20 | .idea/**/sqlDataSources.xml 21 | .idea/**/dynamic.xml 22 | .idea/**/uiDesigner.xml 23 | .idea/**/dbnavigator.xml 24 | 25 | # Gradle 26 | .idea/**/gradle.xml 27 | .idea/**/libraries 28 | 29 | # Gradle and Maven with auto-import 30 | # When using Gradle or Maven with auto-import, you should exclude module files, 31 | # since they will be recreated, and may cause churn. Uncomment if using 32 | # auto-import. 33 | .idea/modules.xml 34 | .idea/*.iml 35 | .idea/modules 36 | *.iml 37 | *.ipr 38 | 39 | # CMake 40 | cmake-build-*/ 41 | 42 | # Mongo Explorer plugin 43 | .idea/**/mongoSettings.xml 44 | 45 | # File-based project format 46 | *.iws 47 | 48 | # IntelliJ 49 | out/ 50 | 51 | # mpeltonen/sbt-idea plugin 52 | .idea_modules/ 53 | 54 | # JIRA plugin 55 | atlassian-ide-plugin.xml 56 | 57 | # Cursive Clojure plugin 58 | .idea/replstate.xml 59 | 60 | # Crashlytics plugin (for Android Studio and IntelliJ) 61 | com_crashlytics_export_strings.xml 62 | crashlytics.properties 63 | crashlytics-build.properties 64 | fabric.properties 65 | 66 | # Editor-based Rest Client 67 | .idea/httpRequests 68 | 69 | # Android studio 3.1+ serialized cache file 70 | .idea/caches/build_file_checksums.ser 71 | 72 | 73 | ### https://raw.github.com/github/gitignore/5be64312ae8e6afe2b62b550400041e90c69db6a/Java.gitignore 74 | 75 | # Compiled class file 76 | *.class 77 | 78 | # Log file 79 | *.log 80 | 81 | # BlueJ files 82 | *.ctxt 83 | 84 | # Mobile Tools for Java (J2ME) 85 | .mtj.tmp/ 86 | 87 | # Package Files # 88 | *.jar 89 | *.war 90 | *.nar 91 | *.ear 92 | *.zip 93 | *.tar.gz 94 | *.rar 95 | 96 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 97 | hs_err_pid* 98 | 99 | 100 | ### https://raw.github.com/github/gitignore/5be64312ae8e6afe2b62b550400041e90c69db6a/Global/Eclipse.gitignore 101 | 102 | .metadata 103 | bin/ 104 | tmp/ 105 | *.tmp 106 | *.bak 107 | *.swp 108 | *~.nib 109 | local.properties 110 | .settings/ 111 | .loadpath 112 | .recommenders 113 | 114 | # External tool builders 115 | .externalToolBuilders/ 116 | 117 | # Locally stored "Eclipse launch configurations" 118 | *.launch 119 | 120 | # PyDev specific (Python IDE for Eclipse) 121 | *.pydevproject 122 | 123 | # CDT-specific (C/C++ Development Tooling) 124 | .cproject 125 | 126 | # CDT- autotools 127 | .autotools 128 | 129 | # Java annotation processor (APT) 130 | .factorypath 131 | 132 | # PDT-specific (PHP Development Tools) 133 | .buildpath 134 | 135 | # sbteclipse plugin 136 | .target 137 | 138 | # Tern plugin 139 | .tern-project 140 | 141 | # TeXlipse plugin 142 | .texlipse 143 | 144 | # STS (Spring Tool Suite) 145 | .springBeans 146 | 147 | # Code Recommenders 148 | .recommenders/ 149 | 150 | # Annotation Processing 151 | .apt_generated/ 152 | 153 | # Scala IDE specific (Scala & Java development for Eclipse) 154 | .cache-main 155 | .scala_dependencies 156 | .worksheet 157 | 158 | 159 | ### https://raw.github.com/github/gitignore/5be64312ae8e6afe2b62b550400041e90c69db6a/Global/Windows.gitignore 160 | 161 | # Windows thumbnail cache files 162 | Thumbs.db 163 | Thumbs.db:encryptable 164 | ehthumbs.db 165 | ehthumbs_vista.db 166 | 167 | # Dump file 168 | *.stackdump 169 | 170 | # Folder config file 171 | [Dd]esktop.ini 172 | 173 | # Recycle Bin used on file shares 174 | $RECYCLE.BIN/ 175 | 176 | # Windows Installer files 177 | *.cab 178 | *.msi 179 | *.msix 180 | *.msm 181 | *.msp 182 | 183 | # Windows shortcuts 184 | *.lnk 185 | 186 | 187 | ### https://raw.github.com/github/gitignore/5be64312ae8e6afe2b62b550400041e90c69db6a/Global/Linux.gitignore 188 | 189 | *~ 190 | 191 | # temporary files which can be created if a process still has a handle open of a deleted file 192 | .fuse_hidden* 193 | 194 | # KDE directory preferences 195 | .directory 196 | 197 | # Linux trash folder which might appear on any partition or disk 198 | .Trash-* 199 | 200 | # .nfs files are created when an open file is removed but is still being accessed 201 | .nfs* 202 | 203 | 204 | ### https://raw.github.com/github/gitignore/5be64312ae8e6afe2b62b550400041e90c69db6a/Global/macOS.gitignore 205 | 206 | # General 207 | .DS_Store 208 | .AppleDouble 209 | .LSOverride 210 | 211 | # Icon must end with two \r 212 | Icon 213 | 214 | # Thumbnails 215 | ._* 216 | 217 | # Files that might appear in the root of a volume 218 | .DocumentRevisions-V100 219 | .fseventsd 220 | .Spotlight-V100 221 | .TemporaryItems 222 | .Trashes 223 | .VolumeIcon.icns 224 | .com.apple.timemachine.donotpresent 225 | 226 | # Directories potentially created on remote AFP share 227 | .AppleDB 228 | .AppleDesktop 229 | Network Trash Folder 230 | Temporary Items 231 | .apdisk 232 | 233 | 234 | ### https://raw.github.com/github/gitignore/5be64312ae8e6afe2b62b550400041e90c69db6a/Global/NetBeans.gitignore 235 | 236 | **/nbproject/private/ 237 | **/nbproject/Makefile-*.mk 238 | **/nbproject/Package-*.bash 239 | build/ 240 | nbbuild/ 241 | dist/ 242 | nbdist/ 243 | .nb-gradle/ 244 | 245 | 246 | ### https://raw.github.com/github/gitignore/5be64312ae8e6afe2b62b550400041e90c69db6a/Maven.gitignore 247 | 248 | target/ 249 | pom.xml.tag 250 | pom.xml.releaseBackup 251 | pom.xml.versionsBackup 252 | pom.xml.next 253 | release.properties 254 | dependency-reduced-pom.xml 255 | buildNumber.properties 256 | .mvn/timing.properties 257 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 258 | .mvn/wrapper/maven-wrapper.jar 259 | .idea/ 260 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | dist: xenial 3 | jdk: 4 | - openjdk8 5 | - openjdk10 6 | - openjdk11 7 | matrix: 8 | include: 9 | # https://www.deps.co/guides/travis-ci-latest-java/#certificate-issues 10 | - jdk: openjdk9 11 | before_install: 12 | - rm "${JAVA_HOME}/lib/security/cacerts" 13 | - ln -s /etc/ssl/certs/java/cacerts "${JAVA_HOME}/lib/security/cacerts" 14 | - jdk: openjdk12 15 | before_install: 16 | - rm "${JAVA_HOME}/lib/security/cacerts" 17 | - ln -s /etc/ssl/certs/java/cacerts "${JAVA_HOME}/lib/security/cacerts" 18 | - jdk: openjdk13 19 | before_install: 20 | - rm "${JAVA_HOME}/lib/security/cacerts" 21 | - ln -s /etc/ssl/certs/java/cacerts "${JAVA_HOME}/lib/security/cacerts" 22 | 23 | install: true 24 | script: mvn -fae -U -B clean install -P check 25 | after_success: 26 | - curl -Ls https://git.io/deploy-maven-snapshot | bash 27 | cache: 28 | directories: 29 | - "$HOME/.m2" 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright (C) 2013 by 52 North Initiative for Geospatial Open 191 | Source Software GmbH 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019 52°North Spatial Information Research GmbH 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | This project includes: 16 | 17 | FindBugs-jsr305 under The Apache Software License, Version 2.0 18 | Jackson JTS datatype under The Apache Software License, Version 2.0 19 | Jackson-annotations under The Apache Software License, Version 2.0 20 | Jackson-core under The Apache Software License, Version 2.0 21 | jackson-databind under The Apache Software License, Version 2.0 22 | org.locationtech.jts:jts-core under Eclipse Public License, Version 2.0 or Eclipse Distribution License - v 1.0 23 | 24 | -------------------------------------------------------------------------------- /etc/license-header.txt: -------------------------------------------------------------------------------- 1 | Copyright ${year} 52°North Spatial Information Research GmbH 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /etc/notice-template.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019 52°North Spatial Information Research GmbH 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | This project includes: 16 | 17 | #GENERATED_NOTICES# -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | org.n52 22 | parent 23 | 27 24 | 25 | 4.0.0 26 | org.n52.jackson 27 | jackson-datatype-jts 28 | 2019 29 | 2.1.0-SNAPSHOT 30 | Jackson JTS datatype 31 | Jackson JTS datatype 32 | 33 | 34 | autermann 35 | Christian Autermann 36 | c.autermann@52north.org 37 | ${project.organization.name} 38 | ${project.organization.url} 39 | GMT+1 40 | 41 | 42 | 43 | https://github.com/52North/jackson-datatype-jts 44 | scm:git:https://github.com/52North/jackson-datatype-jts.git 45 | scm:git:https://github.com/52North/jackson-datatype-jts.git 46 | HEAD 47 | 48 | 49 | GitHub issues 50 | https://github.com/52North/jackson-datatype-jts/issues?state=open 51 | 52 | 53 | 54 | The Apache Software License, Version 2.0 55 | http://www.apache.org/licenses/LICENSE-2.0.txt 56 | repo 57 | A business-friendly OSS license 58 | 59 | 60 | 61 | 2.18.3 62 | 63 | 64 | 65 | org.locationtech.jts 66 | jts-core 67 | 1.20.0 68 | 69 | 70 | com.fasterxml.jackson.core 71 | jackson-databind 72 | ${jackson-version} 73 | 74 | 75 | com.fasterxml.jackson.core 76 | jackson-annotations 77 | ${jackson-version} 78 | 79 | 80 | com.fasterxml.jackson.core 81 | jackson-core 82 | ${jackson-version} 83 | 84 | 85 | com.google.code.findbugs 86 | jsr305 87 | 3.0.2 88 | 89 | 90 | 91 | 92 | org.junit.jupiter 93 | junit-jupiter-api 94 | 5.12.1 95 | test 96 | 97 | 98 | org.assertj 99 | assertj-core 100 | 3.27.3 101 | test 102 | 103 | 104 | 105 | install 106 | 107 | 108 | org.codehaus.mojo 109 | templating-maven-plugin 110 | 1.0.0 111 | 112 | 113 | filtering-java-templates 114 | 115 | filter-sources 116 | 117 | 118 | 119 | 120 | 121 | org.apache.maven.plugins 122 | maven-release-plugin 123 | 124 | clean install 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-javadoc-plugin 130 | 131 | -Xdoclint:none 132 | 133 | 134 | 135 | org.codehaus.mojo 136 | build-helper-maven-plugin 137 | 3.6.0 138 | 139 | 140 | timestamp-property 141 | 142 | timestamp-property 143 | 144 | validate 145 | 146 | current.year 147 | yyyy 148 | 149 | 150 | 151 | 152 | 153 | com.mycila 154 | license-maven-plugin 155 | false 156 | 157 | true 158 | 159 | 160 |
etc/license-header.txt
161 | 162 | **/*.java 163 | **/*.xml 164 | 165 |
166 |
167 | true 168 | 169 | ${project.inceptionYear}-${current.year} 170 | 171 | 172 | SLASHSTAR_STYLE 173 | 174 |
175 | 176 | 177 | format-license-headers 178 | process-resources 179 | 180 | format 181 | 182 | 183 | 184 |
185 | 186 | org.jasig.maven 187 | maven-notice-plugin 188 | 1.1.0 189 | false 190 | 191 | etc/notice-template.txt 192 | 193 | http://52north.github.io/cdn/licenses/license-mappings.xml 194 | 195 | 196 | 197 | 198 | generate-notice 199 | initialize 200 | 201 | generate 202 | 203 | 204 | 205 | 206 | 207 | com.sun.istack 208 | istack-commons-runtime 209 | 4.2.0 210 | runtime 211 | 212 | 213 | javax.xml.bind 214 | jaxb-api 215 | 2.4.0-b180830.0359 216 | 217 | 218 | javax.activation 219 | activation 220 | 1.1.1 221 | 222 | 223 | org.glassfish.jaxb 224 | jaxb-runtime 225 | 2.4.0-b180830.0438 226 | 227 | 228 | 229 |
230 |
231 |
232 | -------------------------------------------------------------------------------- /src/main/java-templates/org/n52/jackson/datatype/jts/VersionInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | import com.fasterxml.jackson.core.Version; 19 | 20 | interface VersionInfo { 21 | String GROUP_ID = "${project.groupId}"; 22 | String ARTIFACT_ID = "${project.artifactId}"; 23 | String VERSION = "${project.version}"; 24 | 25 | static Version getVersion() { 26 | String[] s = VersionInfo.VERSION.split("-"); 27 | String snapshotInfo = null; 28 | int major = 0; 29 | int minor = 0; 30 | int patch = 0; 31 | if (s.length > 1) { 32 | snapshotInfo = s[1]; 33 | } 34 | String[] v = s[0].split("\\."); 35 | 36 | if (v.length >= 3) { 37 | try { 38 | patch = Integer.parseInt(v[2], 10); 39 | } catch (IllegalArgumentException ignored) { 40 | } 41 | } 42 | if (v.length >= 2) { 43 | try { 44 | minor = Integer.parseInt(v[1], 10); 45 | } catch (IllegalArgumentException ignored) { 46 | } 47 | } 48 | if (v.length >= 1) { 49 | try { 50 | major = Integer.parseInt(v[0], 10); 51 | } catch (IllegalArgumentException ignored) { 52 | } 53 | } 54 | 55 | return new Version(major, minor, patch, snapshotInfo, GROUP_ID, ARTIFACT_ID); 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/java/org/n52/jackson/datatype/jts/Field.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | /** 19 | * JSON field names. 20 | */ 21 | public interface Field { 22 | /** 23 | * The {@value} field. 24 | */ 25 | String TYPE = "type"; 26 | /** 27 | * The {@value} field. 28 | */ 29 | String GEOMETRIES = "geometries"; 30 | /** 31 | * The {@value} field. 32 | */ 33 | String COORDINATES = "coordinates"; 34 | /** 35 | * The {@value} field. 36 | */ 37 | String BOUNDING_BOX = "bbox"; 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/n52/jackson/datatype/jts/GeometryDeserializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | import com.fasterxml.jackson.core.JsonParser; 19 | import com.fasterxml.jackson.databind.DeserializationContext; 20 | import com.fasterxml.jackson.databind.JsonDeserializer; 21 | import com.fasterxml.jackson.databind.JsonMappingException; 22 | import com.fasterxml.jackson.databind.JsonNode; 23 | import org.locationtech.jts.geom.Coordinate; 24 | import org.locationtech.jts.geom.Geometry; 25 | import org.locationtech.jts.geom.GeometryCollection; 26 | import org.locationtech.jts.geom.GeometryFactory; 27 | import org.locationtech.jts.geom.LineString; 28 | import org.locationtech.jts.geom.LinearRing; 29 | import org.locationtech.jts.geom.MultiLineString; 30 | import org.locationtech.jts.geom.MultiPoint; 31 | import org.locationtech.jts.geom.MultiPolygon; 32 | import org.locationtech.jts.geom.Point; 33 | import org.locationtech.jts.geom.Polygon; 34 | import org.locationtech.jts.geom.PrecisionModel; 35 | 36 | import javax.annotation.Nullable; 37 | import java.io.IOException; 38 | import java.util.Optional; 39 | 40 | /** 41 | * {@link JsonDeserializer} for {@link Geometry}. 42 | * 43 | * @author Christian Autermann 44 | */ 45 | public class GeometryDeserializer extends JsonDeserializer { 46 | private static final int DEFAULT_SRID = 4326; 47 | private static final GeometryFactory DEFAULT_GEOMETRY_FACTORY = getDefaultGeometryFactory(); 48 | private final GeometryFactory geometryFactory; 49 | 50 | /** 51 | * Creates a new {@link GeometryDeserializer}. 52 | */ 53 | public GeometryDeserializer() { 54 | this(null); 55 | } 56 | 57 | /** 58 | * Creates a new {@link GeometryDeserializer}. 59 | * 60 | * @param geometryFactory The {@link GeometryFactory} to use to construct geometries. 61 | */ 62 | public GeometryDeserializer(@Nullable GeometryFactory geometryFactory) { 63 | this.geometryFactory = Optional.ofNullable(geometryFactory).orElse(DEFAULT_GEOMETRY_FACTORY); 64 | } 65 | 66 | @Override 67 | public Geometry deserialize(JsonParser p, DeserializationContext context) throws IOException { 68 | return deserializeGeometry(p.readValueAs(JsonNode.class), context); 69 | } 70 | 71 | private Geometry deserializeGeometry(JsonNode node, DeserializationContext context) 72 | throws JsonMappingException { 73 | String typeName = node.get(Field.TYPE).asText(); 74 | 75 | GeometryType type = GeometryType.fromString(typeName) 76 | .orElseThrow(() -> invalidGeometryType(context, typeName)); 77 | 78 | switch (type) { 79 | case POINT: 80 | return deserializePoint(node, context); 81 | case MULTI_POINT: 82 | return deserializeMultiPoint(node, context); 83 | case LINE_STRING: 84 | return deserializeLineString(node, context); 85 | case MULTI_LINE_STRING: 86 | return deserializeMultiLineString(node, context); 87 | case POLYGON: 88 | return deserializePolygon(node, context); 89 | case MULTI_POLYGON: 90 | return deserializeMultiPolygon(node, context); 91 | case GEOMETRY_COLLECTION: 92 | return deserializeGeometryCollection(node, context); 93 | default: 94 | throw invalidGeometryType(context, typeName); 95 | } 96 | } 97 | 98 | private JsonMappingException invalidGeometryType(DeserializationContext context, String typeName) { 99 | return JsonMappingException.from(context, "Invalid geometry type: " + typeName); 100 | } 101 | 102 | private Point deserializePoint(JsonNode node, DeserializationContext context) throws JsonMappingException { 103 | JsonNode coordinates = getArray(node, context, Field.COORDINATES); 104 | return this.geometryFactory.createPoint(deserializeCoordinate(coordinates, context)); 105 | } 106 | 107 | private Polygon deserializePolygon(JsonNode node, DeserializationContext context) throws JsonMappingException { 108 | JsonNode coordinates = getArray(node, context, Field.COORDINATES); 109 | return deserializeLinearRings(coordinates, context); 110 | } 111 | 112 | private MultiPolygon deserializeMultiPolygon(JsonNode node, DeserializationContext context) 113 | throws JsonMappingException { 114 | JsonNode coordinates = getArray(node, context, Field.COORDINATES); 115 | Polygon[] polygons = new Polygon[coordinates.size()]; 116 | for (int i = 0; i != coordinates.size(); ++i) { 117 | polygons[i] = deserializeLinearRings(coordinates.get(i), context); 118 | } 119 | return this.geometryFactory.createMultiPolygon(polygons); 120 | } 121 | 122 | private MultiPoint deserializeMultiPoint(JsonNode node, DeserializationContext context) 123 | throws JsonMappingException { 124 | JsonNode coordinates = getArray(node, context, Field.COORDINATES); 125 | Coordinate[] coords = deserializeCoordinates(coordinates, context); 126 | return this.geometryFactory.createMultiPointFromCoords(coords); 127 | } 128 | 129 | private GeometryCollection deserializeGeometryCollection(JsonNode node, DeserializationContext context) 130 | throws JsonMappingException { 131 | JsonNode geometries = getArray(node, context, Field.GEOMETRIES); 132 | Geometry[] geom = new Geometry[geometries.size()]; 133 | for (int i = 0; i != geometries.size(); ++i) { 134 | geom[i] = deserializeGeometry(geometries.get(i), context); 135 | } 136 | return this.geometryFactory.createGeometryCollection(geom); 137 | } 138 | 139 | private MultiLineString deserializeMultiLineString(JsonNode node, DeserializationContext context) 140 | throws JsonMappingException { 141 | JsonNode coordinates = getArray(node, context, Field.COORDINATES); 142 | LineString[] lineStrings = lineStringsFromJson(coordinates, context); 143 | return this.geometryFactory.createMultiLineString(lineStrings); 144 | } 145 | 146 | private LineString[] lineStringsFromJson(JsonNode node, DeserializationContext context) 147 | throws JsonMappingException { 148 | LineString[] strings = new LineString[node.size()]; 149 | for (int i = 0; i != node.size(); ++i) { 150 | Coordinate[] coordinates = deserializeCoordinates(node.get(i), context); 151 | strings[i] = this.geometryFactory.createLineString(coordinates); 152 | } 153 | return strings; 154 | } 155 | 156 | private LineString deserializeLineString(JsonNode node, DeserializationContext context) 157 | throws JsonMappingException { 158 | JsonNode coordinates = getArray(node, context, Field.COORDINATES); 159 | Coordinate[] coords = deserializeCoordinates(coordinates, context); 160 | return this.geometryFactory.createLineString(coords); 161 | } 162 | 163 | private Coordinate[] deserializeCoordinates(JsonNode node, DeserializationContext context) 164 | throws JsonMappingException { 165 | Coordinate[] points = new Coordinate[node.size()]; 166 | for (int i = 0; i != node.size(); ++i) { 167 | points[i] = deserializeCoordinate(node.get(i), context); 168 | } 169 | return points; 170 | } 171 | 172 | private Polygon deserializeLinearRings(JsonNode node, DeserializationContext context) 173 | throws JsonMappingException { 174 | LinearRing shell = deserializeLinearRing(node.get(0), context); 175 | LinearRing[] holes = new LinearRing[node.size() - 1]; 176 | for (int i = 1; i < node.size(); ++i) { 177 | holes[i - 1] = deserializeLinearRing(node.get(i), context); 178 | } 179 | return this.geometryFactory.createPolygon(shell, holes); 180 | } 181 | 182 | private LinearRing deserializeLinearRing(JsonNode node, DeserializationContext context) 183 | throws JsonMappingException { 184 | Coordinate[] coordinates = deserializeCoordinates(node, context); 185 | return this.geometryFactory.createLinearRing(coordinates); 186 | } 187 | 188 | private Coordinate deserializeCoordinate(JsonNode node, DeserializationContext context) 189 | throws JsonMappingException { 190 | if (node.size() < 2) { 191 | throw JsonMappingException.from(context, String.format("Invalid number of ordinates: %d", node.size())); 192 | } else { 193 | double x = getOrdinate(node, 0, context); 194 | double y = getOrdinate(node, 1, context); 195 | if (node.size() < 3) { 196 | return new Coordinate(x, y); 197 | } else { 198 | double z = getOrdinate(node, 2, context); 199 | return new Coordinate(x, y, z); 200 | } 201 | } 202 | } 203 | 204 | private JsonNode getArray(JsonNode node, DeserializationContext context, String fieldName) 205 | throws JsonMappingException { 206 | JsonNode coordinates = node.get(fieldName); 207 | if (coordinates != null && !coordinates.isArray()) { 208 | throw JsonMappingException.from(context, "Invalid coordinates, expecting an array but got: " 209 | + coordinates.getNodeType().toString()); 210 | } 211 | return coordinates; 212 | } 213 | 214 | private double getOrdinate(JsonNode node, int i, DeserializationContext context) throws JsonMappingException { 215 | JsonNode ordinate = node.get(i); 216 | if (!ordinate.isNumber()) { 217 | throw JsonMappingException.from(context, "Invalid coordinates, expecting numbers but got: " 218 | + ordinate.getNodeType().toString()); 219 | } 220 | return ordinate.asDouble(); 221 | } 222 | 223 | private static GeometryFactory getDefaultGeometryFactory() { 224 | return new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), DEFAULT_SRID); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/main/java/org/n52/jackson/datatype/jts/GeometrySerializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | import com.fasterxml.jackson.core.JsonGenerator; 19 | import com.fasterxml.jackson.databind.JsonMappingException; 20 | import com.fasterxml.jackson.databind.JsonSerializer; 21 | import com.fasterxml.jackson.databind.SerializerProvider; 22 | import org.locationtech.jts.geom.Coordinate; 23 | import org.locationtech.jts.geom.CoordinateSequence; 24 | import org.locationtech.jts.geom.Envelope; 25 | import org.locationtech.jts.geom.Geometry; 26 | import org.locationtech.jts.geom.GeometryCollection; 27 | import org.locationtech.jts.geom.LineString; 28 | import org.locationtech.jts.geom.MultiLineString; 29 | import org.locationtech.jts.geom.MultiPoint; 30 | import org.locationtech.jts.geom.MultiPolygon; 31 | import org.locationtech.jts.geom.Point; 32 | import org.locationtech.jts.geom.Polygon; 33 | 34 | import javax.annotation.Nullable; 35 | import java.io.IOException; 36 | import java.math.RoundingMode; 37 | import java.text.DecimalFormat; 38 | import java.text.NumberFormat; 39 | import java.util.Locale; 40 | import java.util.Optional; 41 | 42 | /** 43 | * {@link JsonSerializer} for {@link Geometry}. 44 | * 45 | * @author Christian Autermann 46 | */ 47 | public class GeometrySerializer extends JsonSerializer { 48 | static final int DEFAULT_DECIMAL_PLACES = 8; 49 | private final NumberFormat decimalFormat; 50 | private final IncludeBoundingBox includeBoundingBox; 51 | 52 | /** 53 | * Creates a new {@link GeometrySerializer}. 54 | */ 55 | public GeometrySerializer() { 56 | this(null, DEFAULT_DECIMAL_PLACES); 57 | } 58 | 59 | /** 60 | * Creates a new {@link GeometrySerializer}. 61 | * 62 | * @param includeBoundingBox when to include a bounding box for a {@link Geometry}. 63 | */ 64 | public GeometrySerializer(@Nullable IncludeBoundingBox includeBoundingBox) { 65 | this(includeBoundingBox, DEFAULT_DECIMAL_PLACES); 66 | } 67 | 68 | /** 69 | * Creates a new {@link GeometrySerializer}. 70 | * 71 | * @param includeBoundingBox when to include a bounding box for a {@link Geometry}. 72 | * @param decimalPlaces The number of decimal places for encoded coordinates. 73 | */ 74 | public GeometrySerializer(@Nullable IncludeBoundingBox includeBoundingBox, int decimalPlaces) { 75 | this.includeBoundingBox = Optional.ofNullable(includeBoundingBox).orElseGet(IncludeBoundingBox::never); 76 | if (decimalPlaces < 0) { 77 | throw new IllegalArgumentException("decimalPlaces < 0"); 78 | } 79 | this.decimalFormat = createNumberFormat(decimalPlaces); 80 | } 81 | 82 | private NumberFormat createNumberFormat(int decimalPlaces) { 83 | var format = DecimalFormat.getInstance(Locale.ROOT); 84 | format.setRoundingMode(RoundingMode.HALF_UP); 85 | format.setMinimumFractionDigits(0); 86 | format.setMaximumFractionDigits(decimalPlaces); 87 | format.setGroupingUsed(false); 88 | return format; 89 | } 90 | 91 | @Override 92 | public Class handledType() { 93 | return Geometry.class; 94 | } 95 | 96 | @Override 97 | public void serialize(Geometry geometry, JsonGenerator generator, SerializerProvider provider) throws IOException { 98 | if (geometry == null) { 99 | generator.writeNull(); 100 | } else if (geometry instanceof Polygon) { 101 | serialize((Polygon) geometry, generator, provider); 102 | } else if (geometry instanceof Point) { 103 | serialize((Point) geometry, generator, provider); 104 | } else if (geometry instanceof MultiPoint) { 105 | serialize((MultiPoint) geometry, generator, provider); 106 | } else if (geometry instanceof MultiPolygon) { 107 | serialize((MultiPolygon) geometry, generator, provider); 108 | } else if (geometry instanceof LineString) { 109 | serialize((LineString) geometry, generator, provider); 110 | } else if (geometry instanceof MultiLineString) { 111 | serialize((MultiLineString) geometry, generator, provider); 112 | } else if (geometry instanceof GeometryCollection) { 113 | serialize((GeometryCollection) geometry, generator, provider); 114 | } else { 115 | throw JsonMappingException.from(generator, 116 | String.format("Geometry type %s is not supported.", 117 | geometry.getClass().getName())); 118 | } 119 | } 120 | 121 | private void serialize(GeometryCollection value, JsonGenerator generator, SerializerProvider provider) 122 | throws IOException { 123 | generator.writeStartObject(); 124 | 125 | serializeTypeAndBoundingBox(GeometryType.GEOMETRY_COLLECTION, value, generator); 126 | 127 | generator.writeArrayFieldStart(Field.GEOMETRIES); 128 | 129 | for (int i = 0; i != value.getNumGeometries(); ++i) { 130 | serialize(value.getGeometryN(i), generator, provider); 131 | } 132 | 133 | generator.writeEndArray(); 134 | generator.writeEndObject(); 135 | } 136 | 137 | private void serialize(MultiPoint value, JsonGenerator generator, SerializerProvider provider) 138 | throws IOException { 139 | generator.writeStartObject(); 140 | serializeTypeAndBoundingBox(GeometryType.MULTI_POINT, value, generator); 141 | generator.writeArrayFieldStart(Field.COORDINATES); 142 | 143 | for (int i = 0; i < value.getNumGeometries(); ++i) { 144 | serializeCoordinate((Point) value.getGeometryN(i), generator, provider); 145 | } 146 | 147 | generator.writeEndArray(); 148 | generator.writeEndObject(); 149 | } 150 | 151 | private void serialize(MultiLineString value, JsonGenerator generator, SerializerProvider provider) 152 | throws IOException { 153 | generator.writeStartObject(); 154 | serializeTypeAndBoundingBox(GeometryType.MULTI_LINE_STRING, value, generator); 155 | generator.writeArrayFieldStart(Field.COORDINATES); 156 | for (int i = 0; i < value.getNumGeometries(); ++i) { 157 | serializeCoordinates((LineString) value.getGeometryN(i), generator, provider); 158 | } 159 | generator.writeEndArray(); 160 | generator.writeEndObject(); 161 | } 162 | 163 | private void serialize(MultiPolygon value, JsonGenerator generator, SerializerProvider provider) 164 | throws IOException { 165 | generator.writeStartObject(); 166 | serializeTypeAndBoundingBox(GeometryType.MULTI_POLYGON, value, generator); 167 | generator.writeArrayFieldStart(Field.COORDINATES); 168 | for (int i = 0; i < value.getNumGeometries(); ++i) { 169 | serializeCoordinates((Polygon) value.getGeometryN(i), generator, provider); 170 | } 171 | generator.writeEndArray(); 172 | generator.writeEndObject(); 173 | } 174 | 175 | private void serialize(Polygon value, JsonGenerator generator, SerializerProvider provider) 176 | throws IOException { 177 | generator.writeStartObject(); 178 | serializeTypeAndBoundingBox(GeometryType.POLYGON, value, generator); 179 | generator.writeFieldName(Field.COORDINATES); 180 | GeometrySerializer.this.serializeCoordinates(value, generator, provider); 181 | generator.writeEndObject(); 182 | } 183 | 184 | private void serialize(LineString value, JsonGenerator generator, SerializerProvider provider) 185 | throws IOException { 186 | generator.writeStartObject(); 187 | serializeTypeAndBoundingBox(GeometryType.LINE_STRING, value, generator); 188 | generator.writeFieldName(Field.COORDINATES); 189 | serializeCoordinates(value, generator, provider); 190 | generator.writeEndObject(); 191 | } 192 | 193 | private void serialize(Point value, JsonGenerator generator, SerializerProvider provider) 194 | throws IOException { 195 | generator.writeStartObject(); 196 | serializeTypeAndBoundingBox(GeometryType.POINT, value, generator); 197 | generator.writeFieldName(Field.COORDINATES); 198 | serializeCoordinate(value, generator, provider); 199 | generator.writeEndObject(); 200 | } 201 | 202 | private void serializeTypeAndBoundingBox(GeometryType type, Geometry geometry, JsonGenerator generator) 203 | throws IOException { 204 | 205 | generator.writeStringField(Field.TYPE, type.toString()); 206 | 207 | if (this.includeBoundingBox.shouldIncludeBoundingBoxFor(type) && !geometry.isEmpty()) { 208 | Envelope envelope = geometry.getEnvelopeInternal(); 209 | generator.writeArrayFieldStart(Field.BOUNDING_BOX); 210 | generator.writeNumber(envelope.getMinX()); 211 | generator.writeNumber(envelope.getMinY()); 212 | generator.writeNumber(envelope.getMaxX()); 213 | generator.writeNumber(envelope.getMaxY()); 214 | generator.writeEndArray(); 215 | } 216 | } 217 | 218 | private void serializeCoordinates(Polygon value, JsonGenerator generator, SerializerProvider provider) 219 | throws IOException { 220 | generator.writeStartArray(); 221 | if (!value.isEmpty()) { 222 | serializeCoordinates(value.getExteriorRing(), generator, provider); 223 | 224 | for (int i = 0; i < value.getNumInteriorRing(); ++i) { 225 | serializeCoordinates(value.getInteriorRingN(i), generator, provider); 226 | } 227 | } 228 | generator.writeEndArray(); 229 | } 230 | 231 | private void serializeCoordinates(LineString value, JsonGenerator generator, SerializerProvider provider) 232 | throws IOException { 233 | serializeCoordinates(value.getCoordinateSequence(), generator, provider); 234 | } 235 | 236 | private void serializeCoordinates(CoordinateSequence value, JsonGenerator generator, SerializerProvider provider) 237 | throws IOException { 238 | generator.writeStartArray(); 239 | for (int i = 0; i < value.size(); ++i) { 240 | serializeCoordinate(value.getCoordinate(i), generator, provider); 241 | } 242 | generator.writeEndArray(); 243 | } 244 | 245 | private void serializeCoordinate(Point value, JsonGenerator generator, SerializerProvider provider) 246 | throws IOException { 247 | serializeCoordinate(value.getCoordinate(), generator, provider); 248 | } 249 | 250 | private void serializeCoordinate(Coordinate value, JsonGenerator generator, SerializerProvider provider) 251 | throws IOException { 252 | generator.writeStartArray(); 253 | generator.writeNumber(decimalFormat.format(value.getX())); 254 | generator.writeNumber(decimalFormat.format(value.getY())); 255 | if (!Double.isNaN(value.getZ()) && Double.isFinite(value.getZ())) { 256 | generator.writeNumber(decimalFormat.format(value.getZ())); 257 | } 258 | generator.writeEndArray(); 259 | } 260 | 261 | } 262 | -------------------------------------------------------------------------------- /src/main/java/org/n52/jackson/datatype/jts/GeometryType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | import com.fasterxml.jackson.annotation.JsonCreator; 19 | import com.fasterxml.jackson.annotation.JsonValue; 20 | import org.locationtech.jts.geom.Geometry; 21 | import org.locationtech.jts.geom.GeometryCollection; 22 | import org.locationtech.jts.geom.LineString; 23 | import org.locationtech.jts.geom.MultiLineString; 24 | import org.locationtech.jts.geom.MultiPoint; 25 | import org.locationtech.jts.geom.MultiPolygon; 26 | import org.locationtech.jts.geom.Point; 27 | import org.locationtech.jts.geom.Polygon; 28 | 29 | import java.util.Optional; 30 | 31 | public enum GeometryType { 32 | /** 33 | * Geometries of type {@link Point}. 34 | */ 35 | POINT(Type.POINT), 36 | /** 37 | * Geometries of type {@link LineString}. 38 | */ 39 | LINE_STRING(Type.LINE_STRING), 40 | /** 41 | * Geometries of type {@link Polygon}. 42 | */ 43 | POLYGON(Type.POLYGON), 44 | /** 45 | * Geometries of type {@link MultiPoint}. 46 | */ 47 | MULTI_POINT(Type.MULTI_POINT), 48 | /** 49 | * Geometries of type {@link MultiLineString}. 50 | */ 51 | MULTI_LINE_STRING(Type.MULTI_LINE_STRING), 52 | /** 53 | * Geometries of type {@link MultiPolygon}. 54 | */ 55 | MULTI_POLYGON(Type.MULTI_POLYGON), 56 | /** 57 | * Geometries of type {@link GeometryCollection}. 58 | */ 59 | GEOMETRY_COLLECTION(Type.GEOMETRY_COLLECTION); 60 | 61 | private final String type; 62 | 63 | /** 64 | * Create a new {@link GeometryType}. 65 | * 66 | * @param type The string type. 67 | */ 68 | GeometryType(String type) { 69 | this.type = type; 70 | } 71 | 72 | /** 73 | * The bit mask value of this {@link GeometryType}. 74 | * 75 | * @return The bit mask. 76 | */ 77 | int mask() { 78 | return 1 << this.ordinal(); 79 | } 80 | 81 | @Override 82 | @JsonValue 83 | public String toString() { 84 | return this.type; 85 | } 86 | 87 | /** 88 | * Get the geometry type from the supplied GeoJSON type value. 89 | * 90 | * @param value The GeoJSON type. 91 | * @return The {@link GeometryType} 92 | */ 93 | @JsonCreator 94 | public static Optional fromString(String value) { 95 | for (GeometryType type : GeometryType.values()) { 96 | if (type.toString().equals(value)) { 97 | return Optional.of(type); 98 | } 99 | } 100 | return Optional.empty(); 101 | } 102 | 103 | /** 104 | * Get the geometry type from the supplied {@link Geometry}. 105 | * 106 | * @param geometry The {@link Geometry}. 107 | * @return The {@link GeometryType}. 108 | */ 109 | public static Optional forGeometry(Geometry geometry) { 110 | if (geometry == null) { 111 | return Optional.empty(); 112 | } else if (geometry instanceof Polygon) { 113 | return Optional.of(POLYGON); 114 | } else if (geometry instanceof Point) { 115 | return Optional.of(POINT); 116 | } else if (geometry instanceof MultiPoint) { 117 | return Optional.of(MULTI_POINT); 118 | } else if (geometry instanceof MultiPolygon) { 119 | return Optional.of(MULTI_POLYGON); 120 | } else if (geometry instanceof LineString) { 121 | return Optional.of(LINE_STRING); 122 | } else if (geometry instanceof MultiLineString) { 123 | return Optional.of(MULTI_LINE_STRING); 124 | } else if (geometry instanceof GeometryCollection) { 125 | return Optional.of(GEOMETRY_COLLECTION); 126 | } else { 127 | return Optional.empty(); 128 | } 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/org/n52/jackson/datatype/jts/IncludeBoundingBox.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | import org.locationtech.jts.geom.Geometry; 19 | import org.locationtech.jts.geom.GeometryCollection; 20 | import org.locationtech.jts.geom.LineString; 21 | import org.locationtech.jts.geom.MultiLineString; 22 | import org.locationtech.jts.geom.MultiPoint; 23 | import org.locationtech.jts.geom.MultiPolygon; 24 | import org.locationtech.jts.geom.Point; 25 | import org.locationtech.jts.geom.Polygon; 26 | 27 | import java.io.Serializable; 28 | 29 | /** 30 | * Class to determine when to include a bounding box for geometries. 31 | */ 32 | public final class IncludeBoundingBox implements Serializable { 33 | private static final long serialVersionUID = 204690259235746434L; 34 | private final int mask; 35 | 36 | /** 37 | * Create a new {@link IncludeBoundingBox}. 38 | * 39 | * @param mask The bit mask. 40 | */ 41 | private IncludeBoundingBox(int mask) { 42 | this.mask = mask; 43 | } 44 | 45 | /** 46 | * @param type The {@link GeometryType}. 47 | * @return The {@link IncludeBoundingBox}. 48 | */ 49 | private IncludeBoundingBox include(GeometryType type) { 50 | return new IncludeBoundingBox(this.mask | type.mask()); 51 | } 52 | 53 | /** 54 | * Include the bounding box for geometries of type {@link Point}. 55 | * 56 | * @return The {@link IncludeBoundingBox}. 57 | */ 58 | public IncludeBoundingBox forPoint() { 59 | return include(GeometryType.POINT); 60 | } 61 | 62 | /** 63 | * Include the bounding box for geometries of type {@link LineString}. 64 | * 65 | * @return The {@link IncludeBoundingBox}. 66 | */ 67 | public IncludeBoundingBox forLineString() { 68 | return include(GeometryType.LINE_STRING); 69 | } 70 | 71 | /** 72 | * Include the bounding box for geometries of type {@link Polygon}. 73 | * 74 | * @return The {@link IncludeBoundingBox}. 75 | */ 76 | public IncludeBoundingBox forPolygon() { 77 | return include(GeometryType.POLYGON); 78 | } 79 | 80 | /** 81 | * Include the bounding box for geometries of type {@link MultiPoint}. 82 | * 83 | * @return The {@link IncludeBoundingBox}. 84 | */ 85 | public IncludeBoundingBox forMultiPoint() { 86 | return include(GeometryType.MULTI_POINT); 87 | } 88 | 89 | /** 90 | * Include the bounding box for geometries of type {@link MultiLineString}. 91 | * 92 | * @return The {@link IncludeBoundingBox}. 93 | */ 94 | public IncludeBoundingBox forMultiLineString() { 95 | return include(GeometryType.MULTI_LINE_STRING); 96 | } 97 | 98 | /** 99 | * Include the bounding box for geometries of type {@link MultiPolygon}. 100 | * 101 | * @return The {@link IncludeBoundingBox}. 102 | */ 103 | public IncludeBoundingBox forMultiPolygon() { 104 | return include(GeometryType.MULTI_POLYGON); 105 | } 106 | 107 | /** 108 | * Include the bounding box for geometries of type {@link GeometryCollection}. 109 | * 110 | * @return The {@link IncludeBoundingBox}. 111 | */ 112 | public IncludeBoundingBox forGeometryCollection() { 113 | return include(GeometryType.GEOMETRY_COLLECTION); 114 | } 115 | 116 | /** 117 | * Include the bounding box for geometries of type {@link MultiPoint}, {@link MultiLineString}, {@link MultiPolygon} 118 | * and {@link GeometryCollection}. 119 | * 120 | * @return The {@link IncludeBoundingBox}. 121 | */ 122 | public IncludeBoundingBox forMultiGeometry() { 123 | return include(GeometryType.MULTI_POINT) 124 | .include(GeometryType.MULTI_LINE_STRING) 125 | .include(GeometryType.MULTI_POLYGON) 126 | .include(GeometryType.GEOMETRY_COLLECTION); 127 | } 128 | 129 | /** 130 | * Checks if the bounding box should be included for the {@link Geometry}. 131 | * 132 | * @param geometry The {@link Geometry}. 133 | * @return If the bounding box should be included. 134 | */ 135 | public boolean shouldIncludeBoundingBoxFor(Geometry geometry) { 136 | return GeometryType.forGeometry(geometry).map(this::shouldIncludeBoundingBoxFor).orElse(false); 137 | } 138 | 139 | /** 140 | * Checks if the bounding box should be included for the {@link GeometryType} 141 | * 142 | * @param type The {@link GeometryType}. 143 | * @return If the bounding box should be included. 144 | */ 145 | public boolean shouldIncludeBoundingBoxFor(GeometryType type) { 146 | return (this.mask & type.mask()) != 0; 147 | } 148 | 149 | /** 150 | * Never include a bounding box. 151 | * 152 | * @return The {@link IncludeBoundingBox}. 153 | */ 154 | public static IncludeBoundingBox never() { 155 | return new IncludeBoundingBox(0); 156 | } 157 | 158 | /** 159 | * Always include bounding box. 160 | * 161 | * @return The {@link IncludeBoundingBox}. 162 | */ 163 | public static IncludeBoundingBox always() { 164 | return never() 165 | .forGeometryCollection() 166 | .forMultiPolygon() 167 | .forMultiLineString() 168 | .forMultiPoint() 169 | .forPolygon() 170 | .forLineString() 171 | .forPoint(); 172 | } 173 | 174 | /** 175 | * Always include bounding box. 176 | * 177 | * @return The {@link IncludeBoundingBox}. 178 | */ 179 | public static IncludeBoundingBox exceptPoints() { 180 | return never() 181 | .forGeometryCollection() 182 | .forMultiPolygon() 183 | .forMultiLineString() 184 | .forMultiPoint() 185 | .forPolygon() 186 | .forLineString(); 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/org/n52/jackson/datatype/jts/JtsModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | import com.fasterxml.jackson.databind.JsonDeserializer; 19 | import com.fasterxml.jackson.databind.JsonSerializer; 20 | import com.fasterxml.jackson.databind.module.SimpleModule; 21 | import org.locationtech.jts.geom.Geometry; 22 | import org.locationtech.jts.geom.GeometryCollection; 23 | import org.locationtech.jts.geom.GeometryFactory; 24 | import org.locationtech.jts.geom.LineString; 25 | import org.locationtech.jts.geom.MultiLineString; 26 | import org.locationtech.jts.geom.MultiPoint; 27 | import org.locationtech.jts.geom.MultiPolygon; 28 | import org.locationtech.jts.geom.Point; 29 | import org.locationtech.jts.geom.Polygon; 30 | 31 | import javax.annotation.Nullable; 32 | 33 | /** 34 | * {@link com.fasterxml.jackson.databind.Module} to add serialization for JTS geometries. 35 | * 36 | * @author Christian Autermann 37 | */ 38 | public class JtsModule extends SimpleModule { 39 | private static final long serialVersionUID = 1L; 40 | private final GeometryFactory geometryFactory; 41 | private final IncludeBoundingBox includeBoundingBox; 42 | private final int decimalPlaces; 43 | 44 | /** 45 | * Creates a new {@link JtsModule}. 46 | */ 47 | public JtsModule() { 48 | this(null, null, GeometrySerializer.DEFAULT_DECIMAL_PLACES); 49 | } 50 | 51 | /** 52 | * Creates a new {@link JtsModule}. 53 | * 54 | * @param decimalPlaces The number of decimal places for encoded coordinates. 55 | */ 56 | public JtsModule(int decimalPlaces) { 57 | this(null, null, decimalPlaces); 58 | } 59 | 60 | /** 61 | * Creates a new {@link JtsModule}. 62 | * 63 | * @param geometryFactory The {@link GeometryFactory} to use to construct geometries. 64 | */ 65 | public JtsModule(@Nullable GeometryFactory geometryFactory) { 66 | this(geometryFactory, null, GeometrySerializer.DEFAULT_DECIMAL_PLACES); 67 | } 68 | 69 | /** 70 | * Creates a new {@link JtsModule}. 71 | * 72 | * @param geometryFactory The {@link GeometryFactory} to use to construct geometries. 73 | * @param decimalPlaces The number of decimal places for encoded coordinates. 74 | */ 75 | public JtsModule(@Nullable GeometryFactory geometryFactory, int decimalPlaces) { 76 | this(geometryFactory, null, decimalPlaces); 77 | } 78 | 79 | /** 80 | * Creates a new {@link JtsModule}. 81 | * 82 | * @param includeBoundingBox The {@link IncludeBoundingBox} to use to serialize geometries. 83 | */ 84 | public JtsModule(@Nullable IncludeBoundingBox includeBoundingBox) { 85 | this(null, includeBoundingBox, GeometrySerializer.DEFAULT_DECIMAL_PLACES); 86 | } 87 | 88 | /** 89 | * Creates a new {@link JtsModule}. 90 | * 91 | * @param includeBoundingBox The {@link IncludeBoundingBox} to use to serialize geometries. 92 | * @param decimalPlaces The number of decimal places for encoded coordinates. 93 | */ 94 | public JtsModule(@Nullable IncludeBoundingBox includeBoundingBox, int decimalPlaces) { 95 | this(null, includeBoundingBox, decimalPlaces); 96 | } 97 | 98 | /** 99 | * Creates a new {@link JtsModule}. 100 | * 101 | * @param geometryFactory The {@link GeometryFactory} to use to construct geometries. 102 | * @param includeBoundingBox The {@link IncludeBoundingBox} to use to serialize geometries. 103 | */ 104 | public JtsModule(@Nullable GeometryFactory geometryFactory, @Nullable IncludeBoundingBox includeBoundingBox) { 105 | this(geometryFactory, includeBoundingBox, GeometrySerializer.DEFAULT_DECIMAL_PLACES); 106 | } 107 | 108 | /** 109 | * Creates a new {@link JtsModule}. 110 | * 111 | * @param geometryFactory The {@link GeometryFactory} to use to construct geometries. 112 | * @param includeBoundingBox The {@link IncludeBoundingBox} to use to serialize geometries. 113 | */ 114 | public JtsModule(@Nullable GeometryFactory geometryFactory, @Nullable IncludeBoundingBox includeBoundingBox, 115 | int decimalPlaces) { 116 | super(VersionInfo.getVersion()); 117 | this.geometryFactory = geometryFactory; 118 | this.includeBoundingBox = includeBoundingBox; 119 | if (decimalPlaces < 0) { 120 | throw new IllegalArgumentException("decimalPlaces < 0"); 121 | } 122 | this.decimalPlaces = decimalPlaces; 123 | } 124 | 125 | @Override 126 | public void setupModule(SetupContext context) { 127 | var deserializer = getDeserializer(); 128 | addSerializer(Geometry.class, getSerializer()); 129 | addDeserializer(Geometry.class, deserializer); 130 | addDeserializer(Point.class, new TypeSafeJsonDeserializer<>(Point.class, deserializer)); 131 | addDeserializer(LineString.class, new TypeSafeJsonDeserializer<>(LineString.class, deserializer)); 132 | addDeserializer(Polygon.class, new TypeSafeJsonDeserializer<>(Polygon.class, deserializer)); 133 | addDeserializer(MultiPoint.class, new TypeSafeJsonDeserializer<>(MultiPoint.class, deserializer)); 134 | addDeserializer(MultiLineString.class, new TypeSafeJsonDeserializer<>(MultiLineString.class, deserializer)); 135 | addDeserializer(MultiPolygon.class, new TypeSafeJsonDeserializer<>(MultiPolygon.class, deserializer)); 136 | addDeserializer(GeometryCollection.class, 137 | new TypeSafeJsonDeserializer<>(GeometryCollection.class, deserializer)); 138 | super.setupModule(context); 139 | } 140 | 141 | private JsonSerializer getSerializer() { 142 | return new GeometrySerializer(this.includeBoundingBox, this.decimalPlaces); 143 | } 144 | 145 | private JsonDeserializer getDeserializer() { 146 | return new GeometryDeserializer(geometryFactory); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/org/n52/jackson/datatype/jts/Type.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | /** 19 | * GeoJSON types. 20 | */ 21 | public interface Type { 22 | /** 23 | * The type {@value}. 24 | */ 25 | String POINT = "Point"; 26 | /** 27 | * The type {@value}. 28 | */ 29 | String LINE_STRING = "LineString"; 30 | /** 31 | * The type {@value}. 32 | */ 33 | String POLYGON = "Polygon"; 34 | /** 35 | * The type {@value}. 36 | */ 37 | String MULTI_POINT = "MultiPoint"; 38 | /** 39 | * The type {@value}. 40 | */ 41 | String MULTI_LINE_STRING = "MultiLineString"; 42 | /** 43 | * The type {@value}. 44 | */ 45 | String MULTI_POLYGON = "MultiPolygon"; 46 | /** 47 | * The type {@value}. 48 | */ 49 | String GEOMETRY_COLLECTION = "GeometryCollection"; 50 | /** 51 | * The type {@value}. 52 | */ 53 | String FEATURE = "Feature"; 54 | /** 55 | * The type {@value}. 56 | */ 57 | String FEATURE_COLLECTION = "FeatureCollection"; 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/n52/jackson/datatype/jts/TypeSafeJsonDeserializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | import com.fasterxml.jackson.core.JsonParser; 19 | import com.fasterxml.jackson.databind.DeserializationContext; 20 | import com.fasterxml.jackson.databind.JavaType; 21 | import com.fasterxml.jackson.databind.JsonDeserializer; 22 | import com.fasterxml.jackson.databind.JsonMappingException; 23 | import com.fasterxml.jackson.databind.type.TypeFactory; 24 | 25 | import java.io.IOException; 26 | import java.util.Objects; 27 | 28 | /** 29 | * Type safe variant of a JsonDeserializer that checks the parsed value is of the right type. 30 | * 31 | * @param The type to be accepted. 32 | */ 33 | public class TypeSafeJsonDeserializer extends JsonDeserializer { 34 | private final JsonDeserializer delegate; 35 | private final JavaType type; 36 | 37 | /** 38 | * Creates a new {@link TypeSafeJsonDeserializer}. 39 | * 40 | * @param type The type to be accepted. 41 | * @param delegate The {@link JsonDeserializer} to delegate to. 42 | */ 43 | public TypeSafeJsonDeserializer(Class type, JsonDeserializer delegate) { 44 | this(TypeFactory.defaultInstance().constructType(type), delegate); 45 | } 46 | 47 | /** 48 | * Creates a new {@link TypeSafeJsonDeserializer}. 49 | * 50 | * @param type The type to be accepted. 51 | * @param delegate The {@link JsonDeserializer} to delegate to. 52 | */ 53 | public TypeSafeJsonDeserializer(JavaType type, JsonDeserializer delegate) { 54 | this.delegate = Objects.requireNonNull(delegate); 55 | this.type = Objects.requireNonNull(type); 56 | } 57 | 58 | @Override 59 | @SuppressWarnings("unchecked") 60 | public T deserialize(JsonParser p, DeserializationContext context) throws IOException { 61 | Object obj = delegate.deserialize(p, context); 62 | if (obj == null) { 63 | return null; 64 | } else if (type.isTypeOrSuperTypeOf(obj.getClass())) { 65 | return (T) obj; 66 | } else { 67 | throw JsonMappingException.from(context, String.format("Invalid type for %s: %s", type, obj.getClass())); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module: -------------------------------------------------------------------------------- 1 | org.n52.jackson.datatype.jts.JtsModule -------------------------------------------------------------------------------- /src/test/java/org/n52/jackson/datatype/jts/DeserializeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2025 52°North Spatial Information Research GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.n52.jackson.datatype.jts; 17 | 18 | import com.fasterxml.jackson.databind.JsonMappingException; 19 | import com.fasterxml.jackson.databind.ObjectMapper; 20 | import org.junit.jupiter.api.Test; 21 | import org.locationtech.jts.geom.MultiPolygon; 22 | import org.locationtech.jts.geom.Point; 23 | 24 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 25 | 26 | public class DeserializeTest { 27 | private final ObjectMapper mapper; 28 | 29 | public DeserializeTest() { 30 | mapper = new ObjectMapper(); 31 | mapper.registerModule(new JtsModule()); 32 | } 33 | 34 | @Test 35 | public void throws_for_invalid_coordinates() { 36 | // GIVEN 37 | String json = "{\"multiPolygon\":{\"type\":\"MultiPolygon\"," 38 | + "\"coordinates\":\"[[[" 39 | + "[102.01234567890,2.01234567890]," 40 | + "[103.01234567890,2.01234567890]," 41 | + "[103.01234567890,3.01234567890]," 42 | + "[102.01234567890,3.01234567890]," 43 | + "[102.01234567890,2.01234567890]]]]\"}}}"; 44 | 45 | // WHEN / THEN 46 | assertThatThrownBy(() -> mapper.readValue(json, TestObject.class)) 47 | .isInstanceOf(JsonMappingException.class) 48 | .hasMessageStartingWith("Invalid coordinates, expecting an array but got: STRING"); 49 | } 50 | 51 | @Test 52 | public void throws_for_invalid_coordinate_elements() { 53 | // GIVEN 54 | String json = "{\"point\":{\"type\":\"Point\",\"coordinates\":[[1,2],[3,4]]}}}"; 55 | 56 | // WHEN / THEN 57 | assertThatThrownBy(() -> mapper.readValue(json, TestObject.class)) 58 | .isInstanceOf(JsonMappingException.class) 59 | .hasMessageStartingWith("Invalid coordinates, expecting numbers but got: ARRAY"); 60 | } 61 | 62 | public static class TestObject { 63 | private Point point; 64 | private MultiPolygon multiPolygon; 65 | 66 | public MultiPolygon getMultiPolygon() { 67 | return multiPolygon; 68 | } 69 | 70 | public void setMultiPolygon(MultiPolygon multiPolygon) { 71 | this.multiPolygon = multiPolygon; 72 | } 73 | 74 | public Point getPoint() { 75 | return point; 76 | } 77 | 78 | public void setPoint(Point point) { 79 | this.point = point; 80 | } 81 | } 82 | } 83 | --------------------------------------------------------------------------------