Skip to content

Jackson 3.x GeoJson (de-)serializers #5100

@bjornharvold

Description

@bjornharvold

Going to put this here instead of a PR.

With the release of Spring Boot 4 and migration to Jackson 3, Spring Data MongoDb does not yet have support for GeoJson serializers / deserializers for Jackson 3.

Here is that support:

public class GeoJsonModule {
    public static class serializers extends JacksonModule {
        @Override
        public String getModuleName() {
            return "Spring Data MongoDB GeoJson - Serializers";
        }

        @Override
        public Version version() {
            return new Version(
                    3,
                    2,
                    0,
                    (String)null,
                    "org.springframework.data",
                    "spring-data-mongodb-geojson"
            );
        }

        @Override
        public void setupModule(SetupContext ctx) {
//            this.baseModule.setupModule(context);
            final SimpleSerializers serializers = new SimpleSerializers();

            serializers.addSerializer(GeoJsonPoint.class, new GeoJsonPointSerializer());
            serializers.addSerializer(GeoJsonMultiPoint.class, new GeoJsonMultiPointSerializer());
            serializers.addSerializer(GeoJsonLineString.class, new GeoJsonLineStringSerializer());
            serializers.addSerializer(GeoJsonMultiLineString.class, new GeoJsonMultiLineStringSerializer());
            serializers.addSerializer(GeoJsonPolygon.class, new GeoJsonPolygonSerializer());
            serializers.addSerializer(GeoJsonMultiPolygon.class, new GeoJsonMultiPolygonSerializer());

            ctx.addSerializers(serializers);
        }
    }

    public static class deserializers extends JacksonModule {
        @Override
        public String getModuleName() {
            return "Spring Data MongoDB GeoJson - Deserializers";
        }

        @Override
        public Version version() {
            return new Version(
                    3,
                    2,
                    0,
                    (String)null,
                    "org.springframework.data",
                    "spring-data-mongodb-geojson"
            );
        }

        @Override
        public void setupModule(SetupContext ctx) {
//            this.baseModule.setupModule(context);
            final SimpleDeserializers deserializers = new SimpleDeserializers();

            deserializers.addDeserializer(GeoJsonPoint.class, new GeoJsonPointDeserializer());
            deserializers.addDeserializer(GeoJsonMultiPoint.class, new GeoJsonMultiPointDeserializer());
            deserializers.addDeserializer(GeoJsonLineString.class, new GeoJsonLineStringDeserializer());
            deserializers.addDeserializer(GeoJsonMultiLineString.class, new GeoJsonMultiLineStringDeserializer());
            deserializers.addDeserializer(GeoJsonPolygon.class, new GeoJsonPolygonDeserializer());
            deserializers.addDeserializer(GeoJsonMultiPolygon.class, new GeoJsonMultiPolygonDeserializer());

            ctx.addDeserializers(deserializers);
        }
    }

    private abstract static class GeoJsonDeserializer<T extends GeoJson<?>> extends ValueDeserializer<T> {
        public @Nullable T deserialize(JsonParser jp, @Nullable DeserializationContext ctxt) throws JacksonException {
            JsonNode node = (JsonNode)jp.readValueAsTree();
            JsonNode coordinates = node.get("coordinates");
            return (T)(coordinates != null && coordinates.isArray() ? this.doDeserialize((ArrayNode)coordinates) : null);
        }

        protected abstract @Nullable T doDeserialize(ArrayNode coordinates);

        protected @Nullable GeoJsonPoint toGeoJsonPoint(@Nullable ArrayNode node) {
            return node == null ? null : new GeoJsonPoint(node.get(0).asDouble(), node.get(1).asDouble());
        }

        protected @Nullable Point toPoint(@Nullable ArrayNode node) {
            return node == null ? null : new Point(node.get(0).asDouble(), node.get(1).asDouble());
        }

        protected List<Point> toPoints(@Nullable ArrayNode node) {
            if (node == null) {
                return Collections.emptyList();
            } else {
                List<Point> points = new ArrayList(node.size());

                for(JsonNode coordinatePair : node) {
                    if (coordinatePair.isArray()) {
                        points.add(this.toPoint((ArrayNode)coordinatePair));
                    }
                }

                return points;
            }
        }

        protected GeoJsonLineString toLineString(ArrayNode node) {
            return new GeoJsonLineString(this.toPoints(node));
        }
    }

    private static class GeoJsonPointDeserializer extends GeoJsonDeserializer<GeoJsonPoint> {
        protected @Nullable GeoJsonPoint doDeserialize(ArrayNode coordinates) {
            return this.toGeoJsonPoint(coordinates);
        }
    }

    private static class GeoJsonLineStringDeserializer extends GeoJsonDeserializer<GeoJsonLineString> {
        protected GeoJsonLineString doDeserialize(ArrayNode coordinates) {
            return new GeoJsonLineString(this.toPoints(coordinates));
        }
    }

    private static class GeoJsonMultiPointDeserializer extends GeoJsonDeserializer<GeoJsonMultiPoint> {
        protected GeoJsonMultiPoint doDeserialize(ArrayNode coordinates) {
            return new GeoJsonMultiPoint(this.toPoints(coordinates));
        }
    }

    private static class GeoJsonMultiLineStringDeserializer extends GeoJsonDeserializer<GeoJsonMultiLineString> {
        protected GeoJsonMultiLineString doDeserialize(ArrayNode coordinates) {
            List<GeoJsonLineString> lines = new ArrayList(coordinates.size());

            for(JsonNode lineString : coordinates) {
                if (lineString.isArray()) {
                    lines.add(this.toLineString((ArrayNode)lineString));
                }
            }

            return new GeoJsonMultiLineString(lines);
        }
    }

    private static class GeoJsonPolygonDeserializer extends GeoJsonDeserializer<GeoJsonPolygon> {
        protected @Nullable GeoJsonPolygon doDeserialize(ArrayNode coordinates) {
            Iterator var2 = coordinates.iterator();
            if (var2.hasNext()) {
                JsonNode ring = (JsonNode)var2.next();
                return new GeoJsonPolygon(this.toPoints((ArrayNode)ring));
            } else {
                return null;
            }
        }
    }

    private static class GeoJsonMultiPolygonDeserializer extends GeoJsonDeserializer<GeoJsonMultiPolygon> {
        protected GeoJsonMultiPolygon doDeserialize(ArrayNode coordinates) {
            List<GeoJsonPolygon> polygones = new ArrayList(coordinates.size());

            for(JsonNode polygon : coordinates) {
                for(JsonNode ring : polygon) {
                    polygones.add(new GeoJsonPolygon(this.toPoints((ArrayNode)ring)));
                }
            }

            return new GeoJsonMultiPolygon(polygones);
        }
    }

    private abstract static class GeoJsonSerializer<T extends GeoJson<? extends Iterable>> extends ValueSerializer<T> {
        @Override
        public void serialize(T shape, JsonGenerator jsonGenerator, SerializationContext ctxt) throws JacksonException {
            jsonGenerator.writeStartObject();
            jsonGenerator.writeStringProperty("type", shape.getType());
            jsonGenerator.writeArrayPropertyStart("coordinates");
            this.doSerialize(shape, jsonGenerator);
            jsonGenerator.writeEndArray();
            jsonGenerator.writeEndObject();
        }

        protected abstract void doSerialize(T shape, JsonGenerator jsonGenerator) throws JacksonException;

        protected void writePoint(Point point, JsonGenerator jsonGenerator) throws JacksonException {
            jsonGenerator.writeStartArray();
            this.writeRawCoordinates(point, jsonGenerator);
            jsonGenerator.writeEndArray();
        }

        protected void writeRawCoordinates(Point point, JsonGenerator jsonGenerator) throws JacksonException {
            jsonGenerator.writeNumber(point.getX());
            jsonGenerator.writeNumber(point.getY());
        }

        protected void writeLine(Iterable<Point> points, JsonGenerator jsonGenerator) throws JacksonException {
            jsonGenerator.writeStartArray();
            this.writeRawLine(points, jsonGenerator);
            jsonGenerator.writeEndArray();
        }

        protected void writeRawLine(Iterable<Point> points, JsonGenerator jsonGenerator) throws JacksonException {
            for(Point point : points) {
                this.writePoint(point, jsonGenerator);
            }

        }
    }

    static class GeoJsonPointSerializer extends GeoJsonSerializer<GeoJsonPoint> {
        protected void doSerialize(GeoJsonPoint value, JsonGenerator jsonGenerator) throws JacksonException {
            this.writeRawCoordinates(value, jsonGenerator);
        }
    }

    static class GeoJsonLineStringSerializer extends GeoJsonSerializer<GeoJsonLineString> {
        protected void doSerialize(GeoJsonLineString value, JsonGenerator jsonGenerator) throws JacksonException {
            this.writeRawLine(value.getCoordinates(), jsonGenerator);
        }
    }

    static class GeoJsonMultiPointSerializer extends GeoJsonSerializer<GeoJsonMultiPoint> {
        protected void doSerialize(GeoJsonMultiPoint value, JsonGenerator jsonGenerator) throws JacksonException {
            this.writeRawLine(value.getCoordinates(), jsonGenerator);
        }
    }

    static class GeoJsonMultiLineStringSerializer extends GeoJsonSerializer<GeoJsonMultiLineString> {
        protected void doSerialize(GeoJsonMultiLineString value, JsonGenerator jsonGenerator) throws JacksonException {
            for(GeoJsonLineString lineString : value.getCoordinates()) {
                this.writeLine(lineString.getCoordinates(), jsonGenerator);
            }

        }
    }

    static class GeoJsonPolygonSerializer extends GeoJsonSerializer<GeoJsonPolygon> {
        protected void doSerialize(GeoJsonPolygon value, JsonGenerator jsonGenerator) throws JacksonException {
            for(GeoJsonLineString lineString : value.getCoordinates()) {
                this.writeLine(lineString.getCoordinates(), jsonGenerator);
            }

        }
    }

    static class GeoJsonMultiPolygonSerializer extends GeoJsonSerializer<GeoJsonMultiPolygon> {
        protected void doSerialize(GeoJsonMultiPolygon value, JsonGenerator jsonGenerator) throws JacksonException {
            for(GeoJsonPolygon polygon : value.getCoordinates()) {
                jsonGenerator.writeStartArray();

                for(GeoJsonLineString lineString : polygon.getCoordinates()) {
                    this.writeLine(lineString.getCoordinates(), jsonGenerator);
                }

                jsonGenerator.writeEndArray();
            }

        }
    }
}

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions