diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java index 498317a80..0a57247c9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java @@ -21,10 +21,16 @@ import java.util.Collections; import java.util.List; import org.springframework.data.geo.Point; +import org.springframework.data.mongodb.core.geo.GeoJsonSerializersModule.GeoJsonLineStringSerializer; +import org.springframework.data.mongodb.core.geo.GeoJsonSerializersModule.GeoJsonMultiLineStringSerializer; +import org.springframework.data.mongodb.core.geo.GeoJsonSerializersModule.GeoJsonMultiPointSerializer; +import org.springframework.data.mongodb.core.geo.GeoJsonSerializersModule.GeoJsonMultiPolygonSerializer; +import org.springframework.data.mongodb.core.geo.GeoJsonSerializersModule.GeoJsonPointSerializer; +import org.springframework.data.mongodb.core.geo.GeoJsonSerializersModule.GeoJsonPolygonSerializer; import org.springframework.lang.Nullable; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; @@ -34,7 +40,10 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.node.ArrayNode; /** - * A Jackson {@link Module} to register custom {@link JsonSerializer} and {@link JsonDeserializer}s for GeoJSON types. + * A Jackson {@link Module} to register custom {@link JsonDeserializer}s for GeoJSON types. + *

+ * Use {@link #geoJsonModule()} to obtain a {@link Module} containing both {@link JsonSerializer serializers} and + * {@link JsonDeserializer deserializers}. * * @author Christoph Strobl * @author Oliver Gierke @@ -47,12 +56,97 @@ public class GeoJsonModule extends SimpleModule { public GeoJsonModule() { - addDeserializer(GeoJsonPoint.class, new GeoJsonPointDeserializer()); - addDeserializer(GeoJsonMultiPoint.class, new GeoJsonMultiPointDeserializer()); - addDeserializer(GeoJsonLineString.class, new GeoJsonLineStringDeserializer()); - addDeserializer(GeoJsonMultiLineString.class, new GeoJsonMultiLineStringDeserializer()); - addDeserializer(GeoJsonPolygon.class, new GeoJsonPolygonDeserializer()); - addDeserializer(GeoJsonMultiPolygon.class, new GeoJsonMultiPolygonDeserializer()); + registerDeserializersIn(this); + // TODO: add serializers as of next major version (4.0). + } + + /** + * Obtain a {@link Module} containing {@link JsonDeserializer deserializers} for the following {@link GeoJson} types: + *

+ * + * @return a {@link Module} containing {@link JsonDeserializer deserializers} for {@link GeoJson} types. + * @since 3.2 + */ + public static Module deserializers() { + + SimpleModule module = new SimpleModule("Spring Data MongoDB GeoJson - Deserializers", + new Version(3, 2, 0, null, "org.springframework.data", "spring-data-mongodb-geojson")); + registerDeserializersIn(module); + return module; + } + + /** + * Obtain a {@link Module} containing {@link JsonSerializer serializers} for the following {@link GeoJson} types: + * + * + * @return a {@link Module} containing {@link JsonSerializer serializers} for {@link GeoJson} types. + * @since 3.2 + */ + public static Module serializers() { + + SimpleModule module = new SimpleModule("Spring Data MongoDB GeoJson - Serializers", + new Version(3, 2, 0, null, "org.springframework.data", "spring-data-mongodb-geojson")); + registerSerializersIn(module); + return module; + } + + /** + * Obtain a {@link Module} containing {@link JsonSerializer serializers} and {@link JsonDeserializer deserializers} + * for the following {@link GeoJson} types: + * + * + * @return a {@link Module} containing {@link JsonSerializer serializers} and {@link JsonDeserializer deserializers} + * for {@link GeoJson} types. + * @since 3.2 + */ + public static Module geoJsonModule() { + + SimpleModule module = new SimpleModule("Spring Data MongoDB GeoJson", + new Version(3, 2, 0, null, "org.springframework.data", "spring-data-mongodb-geojson")); + registerSerializersIn(module); + registerDeserializersIn(module); + return module; + } + + private static void registerSerializersIn(SimpleModule module) { + + module.addSerializer(GeoJsonPoint.class, new GeoJsonPointSerializer()); + module.addSerializer(GeoJsonMultiPoint.class, new GeoJsonMultiPointSerializer()); + module.addSerializer(GeoJsonLineString.class, new GeoJsonLineStringSerializer()); + module.addSerializer(GeoJsonMultiLineString.class, new GeoJsonMultiLineStringSerializer()); + module.addSerializer(GeoJsonPolygon.class, new GeoJsonPolygonSerializer()); + module.addSerializer(GeoJsonMultiPolygon.class, new GeoJsonMultiPolygonSerializer()); + } + + private static void registerDeserializersIn(SimpleModule module) { + + module.addDeserializer(GeoJsonPoint.class, new GeoJsonPointDeserializer()); + module.addDeserializer(GeoJsonMultiPoint.class, new GeoJsonMultiPointDeserializer()); + module.addDeserializer(GeoJsonLineString.class, new GeoJsonLineStringDeserializer()); + module.addDeserializer(GeoJsonMultiLineString.class, new GeoJsonMultiLineStringDeserializer()); + module.addDeserializer(GeoJsonPolygon.class, new GeoJsonPolygonDeserializer()); + module.addDeserializer(GeoJsonMultiPolygon.class, new GeoJsonMultiPolygonDeserializer()); } /** @@ -67,8 +161,7 @@ public class GeoJsonModule extends SimpleModule { */ @Nullable @Override - public T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext ctxt) - throws IOException, JsonProcessingException { + public T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext ctxt) throws IOException { JsonNode node = jp.readValueAsTree(); JsonNode coordinates = node.get("coordinates"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersModule.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersModule.java index c6b12337c..3f2642997 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersModule.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersModule.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,29 +15,29 @@ */ package org.springframework.data.mongodb.core.geo; +import java.io.IOException; + +import org.springframework.data.geo.Point; + import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; -import org.springframework.data.geo.Point; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; /** * A Jackson {@link Module} to register custom {@link JsonSerializer}s for GeoJSON types. * * @author Bjorn Harvold - * @since + * @author Christoph Strobl + * @since 3.2 */ -public class GeoJsonSerializersModule extends SimpleModule { +class GeoJsonSerializersModule extends SimpleModule { private static final long serialVersionUID = 1340494654898895610L; - public GeoJsonSerializersModule() { + GeoJsonSerializersModule() { + addSerializer(GeoJsonPoint.class, new GeoJsonPointSerializer()); addSerializer(GeoJsonMultiPoint.class, new GeoJsonMultiPointSerializer()); addSerializer(GeoJsonLineString.class, new GeoJsonLineStringSerializer()); @@ -46,111 +46,263 @@ public class GeoJsonSerializersModule extends SimpleModule { addSerializer(GeoJsonMultiPolygon.class, new GeoJsonMultiPolygonSerializer()); } - public static class GeoJsonPointSerializer extends JsonSerializer { + /** + * @param + * @author Christoph Strobl + */ + private static abstract class GeoJsonSerializer> extends JsonSerializer { + + /* + * (non-Javadoc) + * @see com.fasterxml.jackson.databind.JsonSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider) + */ @Override - public void serialize(GeoJsonPoint value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { - gen.writeStartObject(); - gen.writeStringField("type", value.getType()); - gen.writeObjectField("coordinates", value.getCoordinates()); - gen.writeEndObject(); + public void serialize(T shape, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException { + + jsonGenerator.writeStartObject(); + jsonGenerator.writeStringField("type", shape.getType()); + jsonGenerator.writeArrayFieldStart("coordinates"); + + doSerialize(shape, jsonGenerator); + + jsonGenerator.writeEndArray(); + jsonGenerator.writeEndObject(); } - } + /** + * Perform the actual serialization given the {@literal shape} as {@link GeoJson}. + * + * @param shape + * @param jsonGenerator + * @return + */ + protected abstract void doSerialize(T shape, JsonGenerator jsonGenerator) throws IOException; - public static class GeoJsonLineStringSerializer extends JsonSerializer { + /** + * Write a {@link Point} as array.
+ * {@code [10.0, 20.0]} + * + * @param point + * @param jsonGenerator + * @throws IOException + */ + protected void writePoint(Point point, JsonGenerator jsonGenerator) throws IOException { - @Override - public void serialize(GeoJsonLineString value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { - gen.writeStartObject(); - gen.writeStringField("type", value.getType()); - gen.writeArrayFieldStart("coordinates"); - for (Point p : value.getCoordinates()) { - gen.writeObject(new double[]{p.getX(), p.getY()}); + jsonGenerator.writeStartArray(); + writeRawCoordinates(point, jsonGenerator); + jsonGenerator.writeEndArray(); + } + + /** + * Write the {@link Point} coordinates.
+ * {@code 10.0, 20.0} + * + * @param point + * @param jsonGenerator + * @throws IOException + */ + protected void writeRawCoordinates(Point point, JsonGenerator jsonGenerator) throws IOException { + + jsonGenerator.writeNumber(point.getX()); + jsonGenerator.writeNumber(point.getY()); + } + + /** + * Write an {@link Iterable} of {@link Point} as array.
+ * {@code [ [10.0, 20.0], [30.0, 40.0], [50.0, 60.0] ]} + * + * @param points + * @param jsonGenerator + * @throws IOException + */ + protected void writeLine(Iterable points, JsonGenerator jsonGenerator) throws IOException { + + jsonGenerator.writeStartArray(); + writeRawLine(points, jsonGenerator); + jsonGenerator.writeEndArray(); + } + + /** + * Write an {@link Iterable} of {@link Point}.
+ * {@code [10.0, 20.0], [30.0, 40.0], [50.0, 60.0]} + * + * @param points + * @param jsonGenerator + * @throws IOException + */ + protected void writeRawLine(Iterable points, JsonGenerator jsonGenerator) throws IOException { + + for (Point point : points) { + writePoint(point, jsonGenerator); } - gen.writeEndArray(); - gen.writeEndObject(); } } - public static class GeoJsonMultiPointSerializer extends JsonSerializer { + /** + * {@link JsonSerializer} converting {@link GeoJsonPoint} to: + * + *
+	 * 
+	 * { "type": "Point", "coordinates": [10.0, 20.0] }
+	 * 
+	 * 
+ * + * @author Bjorn Harvold + * @author Christoph Strobl + * @since 3.2 + */ + static class GeoJsonPointSerializer extends GeoJsonSerializer { @Override - public void serialize(GeoJsonMultiPoint value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { - gen.writeStartObject(); - gen.writeStringField("type", value.getType()); - gen.writeArrayFieldStart("coordinates"); - for (Point p : value.getCoordinates()) { - gen.writeObject(new double[]{p.getX(), p.getY()}); - } - gen.writeEndArray(); - gen.writeEndObject(); + protected void doSerialize(GeoJsonPoint value, JsonGenerator jsonGenerator) throws IOException { + writeRawCoordinates(value, jsonGenerator); } } - public static class GeoJsonMultiLineStringSerializer extends JsonSerializer { + /** + * {@link JsonSerializer} converting {@link GeoJsonLineString} to: + * + *
+	 * 
+	 * {
+	 *   "type": "LineString",
+	 *   "coordinates": [
+	 *     [10.0, 20.0], [30.0, 40.0], [50.0, 60.0]
+	 *   ]
+	 * }
+	 * 
+	 * 
+ * + * @author Bjorn Harvold + * @author Christoph Strobl + * @since 3.2 + */ + static class GeoJsonLineStringSerializer extends GeoJsonSerializer { @Override - public void serialize(GeoJsonMultiLineString value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { - gen.writeStartObject(); - gen.writeStringField("type", value.getType()); - gen.writeArrayFieldStart("coordinates"); + protected void doSerialize(GeoJsonLineString value, JsonGenerator jsonGenerator) throws IOException { + writeRawLine(value.getCoordinates(), jsonGenerator); + } + } + + /** + * {@link JsonSerializer} converting {@link GeoJsonMultiPoint} to: + * + *
+	 * 
+	 * {
+	 *   "type": "MultiPoint",
+	 *   "coordinates": [
+	 *     [10.0, 20.0], [30.0, 40.0], [50.0, 60.0]
+	 *   ]
+	 * }
+	 * 
+	 * 
+ * + * @author Bjorn Harvold + * @author Christoph Strobl + * @since 3.2 + */ + static class GeoJsonMultiPointSerializer extends GeoJsonSerializer { + + @Override + protected void doSerialize(GeoJsonMultiPoint value, JsonGenerator jsonGenerator) throws IOException { + writeRawLine(value.getCoordinates(), jsonGenerator); + } + } + + /** + * {@link JsonSerializer} converting {@link GeoJsonMultiLineString} to: + * + *
+	 * 
+	 * {
+	 *   "type": "MultiLineString",
+	 *   "coordinates": [
+	 *     [ [10.0, 20.0], [30.0, 40.0] ],
+	 *     [ [50.0, 60.0] , [70.0, 80.0] ]
+	 *   ]
+	 * }
+	 * 
+	 * 
+ * + * @author Bjorn Harvold + * @author Christoph Strobl + * @since 3.2 + */ + static class GeoJsonMultiLineStringSerializer extends GeoJsonSerializer { + + @Override + protected void doSerialize(GeoJsonMultiLineString value, JsonGenerator jsonGenerator) throws IOException { + for (GeoJsonLineString lineString : value.getCoordinates()) { - List arrayList = new ArrayList<>(); - for (Point p : lineString.getCoordinates()) { - arrayList.add(new double[]{p.getX(), p.getY()}); - } - double[][] doubles = arrayList.toArray(new double[0][0]); - gen.writeObject(doubles); + writeLine(lineString.getCoordinates(), jsonGenerator); } - gen.writeEndArray(); - gen.writeEndObject(); } } - public static class GeoJsonPolygonSerializer extends JsonSerializer { + /** + * {@link JsonSerializer} converting {@link GeoJsonPolygon} to: + * + *
+	 * 
+	 * {
+	 *   "type": "Polygon",
+	 *   "coordinates": [
+	 *     [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
+	 *   ]
+	 * }
+	 * 
+	 * 
+ * + * @author Bjorn Harvold + * @author Christoph Strobl + * @since 3.2 + */ + static class GeoJsonPolygonSerializer extends GeoJsonSerializer { @Override - public void serialize(GeoJsonPolygon value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { - gen.writeStartObject(); - gen.writeStringField("type", value.getType()); - gen.writeArrayFieldStart("coordinates"); - for (GeoJsonLineString ls : value.getCoordinates()) { - gen.writeStartArray(); - for (Point p : ls.getCoordinates()) { - gen.writeObject(new double[]{p.getX(), p.getY()}); - } - gen.writeEndArray(); + protected void doSerialize(GeoJsonPolygon value, JsonGenerator jsonGenerator) throws IOException { + + for (GeoJsonLineString lineString : value.getCoordinates()) { + writeLine(lineString.getCoordinates(), jsonGenerator); } - gen.writeEndArray(); - gen.writeEndObject(); } } - public static class GeoJsonMultiPolygonSerializer extends JsonSerializer { + /** + * {@link JsonSerializer} converting {@link GeoJsonMultiPolygon} to: + * + *
+	 * 
+	 * {
+	 *   "type": "MultiPolygon",
+	 *   "coordinates": [
+	 *     [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],
+	 *     [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
+	 *     [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
+	 *   ]
+	 * }
+	 * 
+	 * 
+ * + * @author Bjorn Harvold + * @author Christoph Strobl + * @since 3.2 + */ + static class GeoJsonMultiPolygonSerializer extends GeoJsonSerializer { @Override - public void serialize(GeoJsonMultiPolygon value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { - gen.writeStartObject(); - gen.writeStringField("type", value.getType()); - gen.writeArrayFieldStart("coordinates"); + protected void doSerialize(GeoJsonMultiPolygon value, JsonGenerator jsonGenerator) throws IOException { + for (GeoJsonPolygon polygon : value.getCoordinates()) { - gen.writeStartArray(); - - gen.writeStartArray(); + jsonGenerator.writeStartArray(); for (GeoJsonLineString lineString : polygon.getCoordinates()) { - - for (Point p : lineString.getCoordinates()) { - gen.writeObject(new double[]{p.getX(), p.getY()}); - } - + writeLine(lineString.getCoordinates(), jsonGenerator); } - - gen.writeEndArray(); - gen.writeEndArray(); + jsonGenerator.writeEndArray(); } - gen.writeEndArray(); - gen.writeEndObject(); } } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonModuleRoundTripUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonModuleRoundTripUnitTests.java deleted file mode 100644 index 5ca4bc8a2..000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonModuleRoundTripUnitTests.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2015-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.core.geo; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.data.geo.Point; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Bjorn Harvold - */ -public class GeoJsonModuleRoundTripUnitTests { - - ObjectMapper mapper; - - @BeforeEach - public void setUp() { - - mapper = new ObjectMapper(); - mapper.registerModule(new GeoJsonModule()); - mapper.registerModule(new GeoJsonSerializersModule()); - } - - @Test - public void shouldMarshalJsonPointCorrectly() throws IOException { - - String json = "{\"type\":\"Point\",\"coordinates\":[10.0,20.0]}"; - GeoJsonPoint point = new GeoJsonPoint(10D, 20D); - - assertThat(mapper.readValue(json, GeoJsonPoint.class)).isEqualTo(point); - - String backToJSON = mapper.writeValueAsString(point); - - assertThat(backToJSON).isEqualTo(json); - } - - @Test - public void shouldMarshalGeoJsonLineStringCorrectly() - throws IOException { - - String json = "{\"type\":\"LineString\",\"coordinates\":[[10.0,20.0],[30.0,40.0],[50.0,60.0]]}"; - - GeoJsonLineString lineString = new GeoJsonLineString(Arrays.asList(new Point(10, 20), new Point(30, 40), new Point(50, 60))); - - assertThat(mapper.readValue(json, GeoJsonLineString.class)).isEqualTo(lineString); - - String backToJSON = mapper.writeValueAsString(lineString); - - assertThat(backToJSON).isEqualTo(json); - } - - @Test - public void shouldMarshalGeoJsonMultiPointCorrectly() throws IOException { - - String json = "{\"type\":\"MultiPoint\",\"coordinates\":[[10.0,20.0],[30.0,40.0],[50.0,60.0]]}"; - GeoJsonMultiPoint multiPoint = new GeoJsonMultiPoint(Arrays.asList(new Point(10, 20), new Point(30, 40), new Point(50, 60))); - - assertThat(mapper.readValue(json, GeoJsonMultiPoint.class)).isEqualTo(multiPoint); - - String backToJSON = mapper.writeValueAsString(multiPoint); - - assertThat(backToJSON).isEqualTo(json); - } - - @Test - @SuppressWarnings("unchecked") - public void shouldMarshalGeoJsonMultiLineStringCorrectly() throws IOException { - - String json = "{\"type\":\"MultiLineString\",\"coordinates\":[[[10.0,20.0],[30.0,40.0]],[[50.0,60.0],[70.0,80.0]]]}"; - GeoJsonMultiLineString multiLineString = new GeoJsonMultiLineString(Arrays.asList(new Point(10, 20), new Point(30, 40)), Arrays.asList(new Point(50, 60), new Point(70, 80))); - - assertThat(mapper.readValue(json, GeoJsonMultiLineString.class)).isEqualTo(multiLineString); - - String backToJSON = mapper.writeValueAsString(multiLineString); - - assertThat(backToJSON).isEqualTo(json); - } - - @Test - public void shouldMarshalGeoJsonPolygonCorrectly() throws IOException { - String json = "{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]]}"; - - List points = Arrays.asList(new Point(100, 0), new Point(101, 0), new Point(101, 1), new Point(100, 1), new Point(100, 0)); - GeoJsonPolygon polygon = new GeoJsonPolygon(points); - - assertThat(mapper.readValue(json, GeoJsonPolygon.class)).isEqualTo(polygon); - - String backToJSON = mapper.writeValueAsString(polygon); - - assertThat(backToJSON).isEqualTo(json); - } - - @Test - public void shouldMarshalGeoJsonMultiPolygonCorrectly() - throws IOException { - - String json = "{\"type\":\"MultiPolygon\",\"coordinates\":[" - + "[" - + "[" - + "[102.0,2.0],[103.0,2.0],[103.0,3.0],[102.0,3.0],[102.0,2.0]" - + "]" - + "]," - + "[" - + "[" - + "[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]" - + "]" - + "]," - + "[" - + "[" - + "[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]" - + "]" - + "]" - + "]" - + "}"; - - GeoJsonMultiPolygon multiPolygon = new GeoJsonMultiPolygon(Arrays.asList( - new GeoJsonPolygon(Arrays.asList(new Point(102, 2), new Point(103, 2), new Point(103, 3), new Point(102, 3), - new Point(102, 2))), - new GeoJsonPolygon(Arrays.asList(new Point(100, 0), new Point(101, 0), new Point(101, 1), new Point(100, 1), - new Point(100, 0))), - new GeoJsonPolygon(Arrays.asList(new Point(100.2, 0.2), new Point(100.8, 0.2), new Point(100.8, 0.8), - new Point(100.2, 0.8), new Point(100.2, 0.2))))); - - assertThat(mapper.readValue(json, GeoJsonMultiPolygon.class)).isEqualTo(multiPolygon); - - String backToJSON = mapper.writeValueAsString(multiPolygon); - - assertThat(backToJSON).isEqualTo(json); - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersUnitTests.java new file mode 100644 index 000000000..498193b80 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersUnitTests.java @@ -0,0 +1,111 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.geo; + +import static org.assertj.core.api.Assertions.*; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.geo.Point; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @author Bjorn Harvold + * @author Christoph Strobl + */ +class GeoJsonSerializersUnitTests { + + ObjectMapper mapper; + + @BeforeEach + void beforeEach() { + + mapper = new ObjectMapper(); + mapper.registerModule(new GeoJsonSerializersModule()); + } + + @Test // GH-3517 + void shouldSerializeJsonPointCorrectly() throws IOException { + + GeoJsonPoint geoJsonPoint = new GeoJsonPoint(10D, 20D); + + assertThat(mapper.writeValueAsString(geoJsonPoint)).isEqualTo("{\"type\":\"Point\",\"coordinates\":[10.0,20.0]}"); + } + + @Test // GH-3517 + public void shouldSerializeGeoJsonLineStringCorrectly() throws IOException { + + GeoJsonLineString lineString = new GeoJsonLineString( + Arrays.asList(new Point(10, 20), new Point(30, 40), new Point(50, 60))); + + assertThat(mapper.writeValueAsString(lineString)) + .isEqualTo("{\"type\":\"LineString\",\"coordinates\":[[10.0,20.0],[30.0,40.0],[50.0,60.0]]}"); + } + + @Test // GH-3517 + public void shouldSerializeGeoJsonMultiPointCorrectly() throws IOException { + + GeoJsonMultiPoint multiPoint = new GeoJsonMultiPoint( + Arrays.asList(new Point(10, 20), new Point(30, 40), new Point(50, 60))); + + assertThat(mapper.writeValueAsString(multiPoint)) + .isEqualTo("{\"type\":\"MultiPoint\",\"coordinates\":[[10.0,20.0],[30.0,40.0],[50.0,60.0]]}"); + } + + @Test // GH-3517 + public void shouldSerializeJsonMultiLineStringCorrectly() throws IOException { + + GeoJsonMultiLineString multiLineString = new GeoJsonMultiLineString( + Arrays.asList(new Point(10, 20), new Point(30, 40)), Arrays.asList(new Point(50, 60), new Point(70, 80))); + + assertThat(mapper.writeValueAsString(multiLineString)).isEqualTo( + "{\"type\":\"MultiLineString\",\"coordinates\":[[[10.0,20.0],[30.0,40.0]],[[50.0,60.0],[70.0,80.0]]]}"); + } + + @Test // GH-3517 + public void shouldSerializeGeoJsonPolygonCorrectly() throws IOException { + + List points = Arrays.asList(new Point(100, 0), new Point(101, 0), new Point(101, 1), new Point(100, 1), + new Point(100, 0)); + GeoJsonPolygon polygon = new GeoJsonPolygon(points); + + assertThat(mapper.writeValueAsString(polygon)).isEqualTo("{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]]}"); + } + + @Test // GH-3517 + public void shouldSerializeGeoJsonMultiPolygonCorrectly() throws IOException { + + String json = "{\"type\":\"MultiPolygon\",\"coordinates\":[" + "[" + "[" + + "[102.0,2.0],[103.0,2.0],[103.0,3.0],[102.0,3.0],[102.0,2.0]" + "]" + "]," + "[" + "[" + + "[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]" + "]" + "]," + "[" + "[" + + "[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]" + "]" + "]" + "]" + "}"; + + GeoJsonMultiPolygon multiPolygon = new GeoJsonMultiPolygon(Arrays.asList( + new GeoJsonPolygon(Arrays.asList(new Point(102, 2), new Point(103, 2), new Point(103, 3), new Point(102, 3), + new Point(102, 2))), + new GeoJsonPolygon(Arrays.asList(new Point(100, 0), new Point(101, 0), new Point(101, 1), new Point(100, 1), + new Point(100, 0))), + new GeoJsonPolygon(Arrays.asList(new Point(100.2, 0.2), new Point(100.8, 0.2), new Point(100.8, 0.8), + new Point(100.2, 0.8), new Point(100.2, 0.2))))); + + assertThat(mapper.writeValueAsString(multiPolygon)).isEqualTo(json); + } +} diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index 2500dc76f..aca6fed42 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -1700,6 +1700,43 @@ Returning the 3 Documents just like the GeoJSON variant: <4> Distance from center point in _Kilometers_ - take it times 1000 to match _Meters_ of the GeoJSON variant. ==== +==== GeoJSON Jackson Modules + +By using the <>, Spring Data adds additional Jackson ``Modules``s to the `ObjectMapper` for de-/serializing common types used by the Spring Data domain. +Please refer to the <> section to learn more about the infrastructure setup of this feature. + +The MongoDB module additionally registers ``JsonDeserializer``s for the following GeoJSON types via its `GeoJsonConfiguration` exposing the `GeoJsonModule`. +---- +org.springframework.data.mongodb.core.geo.GeoJsonPoint +org.springframework.data.mongodb.core.geo.GeoJsonMultiPoint +org.springframework.data.mongodb.core.geo.GeoJsonLineString +org.springframework.data.mongodb.core.geo.GeoJsonMultiLineString +org.springframework.data.mongodb.core.geo.GeoJsonPolygon +org.springframework.data.mongodb.core.geo.GeoJsonMultiPolygon +---- + +[NOTE] +==== +The `GeoJsonModule` only registers ``JsonDeserializer``s! + +To equip the `ObjectMapper` with a symmetric set of ``JsonSerializer``s you need to either manually configure those for the `ObjectMapper` or provide a custom `SpringDataJacksonModules` configuration exposing `GeoJsonModule.serializers()` as a Spring Bean. + +[source,java] +---- +class GeoJsonConfiguration implements SpringDataJacksonModules { + + @Bean + public Module geoJsonSerializers() { + return GeoJsonModule.serializers(); + } +} +---- +==== + +[WARNING] +==== +The next major version (`4.0`) will by default register both, ``JsonDeserializer``s and ``JsonSerializer``s for GeoJSON types. +==== + [[mongo.textsearch]] === Full-text Queries