Skip to content

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

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();
            }

        }
    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions