简体   繁体   English

Jackson 在 Spring Boot 中反序列化 GeoJson Point

[英]Jackson deserialize GeoJson Point in Spring Boot

I have a @Entity model that has a property of type com.vividsolutions.jts.geom.Point .我有一个@Entity模型,它具有com.vividsolutions.jts.geom.Point类型的com.vividsolutions.jts.geom.Point When I try to render this model in a @RestController I get a recursion exception.当我尝试在@RestController渲染此模型时,出现递归异常。

(StackOverflowError); nested exception is 
com.fasterxml.jackson.databind.JsonMappingException: Infinite 
recursion (StackOverflowError) (through reference chain: 
com.vividsolutions.jts.geom.Point[\"envelope\"]-
>com.vividsolutions.jts.geom.Point[\"envelope\"]....

The entity looks like this (shortened for brevity):实体看起来像这样(为简洁起见缩短):

@Entity
@Data
public class MyEntity{
    // ...
    @Column(columnDefinition = "geometry")
    private Point location;
    // ...
}

After some research I found out that this is because Jackson cannot deserialize GeoJson by default.经过一番研究,我发现这是因为 Jackson 默认无法反序列化 GeoJson。 Adding this library should solve the issue: https://github.com/bedatadriven/jackson-datatype-jts .添加这个库应该可以解决这个问题: https : //github.com/bedatadriven/jackson-datatype-jts

I am now not sure how to include this module in the object mapper in spring boot.我现在不确定如何在 Spring Boot 的对象映射器中包含这个模块。 As per documentation in boot, I tried adding it to the @Configuration in the following two ways:根据引导中的文档,我尝试通过以下两种方式将其添加到@Configuration中:

@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.modulesToInstall(new JtsModule());
    return builder;
}

and

@Bean
public JtsModule jtsModule(){
    return new JtsModule();
}

Both didn't remove the exception.两者都没有删除异常。 Sry if this is a duplicate, but all I was able to find SO were customising the ObjectMapper which in my understanding of the documentation is no the "spring boot way". Sry 如果这是重复的,但我能找到的所有东西都是自定义ObjectMapper ,根据我对文档的理解,它不是“弹簧启动方式”。

As a workaround I am @JsonIgnore ing the Point and have custom getters and setters for a non existent coordinated object,... but it's not the way I'd like to keep it.作为一种解决方法,我@JsonIgnore Point并为不存在的协调对象自定义 getter 和 setter,...但这不是我想保留它的方式。

Maybe you should tag your geometric attribute with @JsonSerialize and @JsonDeserialize .也许你应该用@JsonSerialize@JsonDeserialize标记你的几何属性。 Like this:像这样:

import com.bedatadriven.jackson.datatype.jts.serialization.GeometryDeserializer;
import com.bedatadriven.jackson.datatype.jts.serialization.GeometrySerializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.vividsolutions.jts.geom.Geometry;
import fr.info.groloc.entity.json.GreffeDeserializer;

import javax.persistence.Entity;

@Entity
public class Table
{
    @JsonSerialize(using = GeometrySerializer.class)
    @JsonDeserialize(contentUsing = GeometryDeserializer.class)
    private Geometry coord;
    // ...
}

If you are using Spring-Boot you only need for:如果您使用的是 Spring-Boot,您只需要:

import com.bedatadriven.jackson.datatype.jts.JtsModule;
// ...
@Bean
public JtsModule jtsModule()
{
    return new JtsModule();
}

As Dave said you need to add this dependency to your pom.xml:正如 Dave 所说,您需要将此依赖项添加到 pom.xml 中:

<dependency>
    <groupId>com.bedatadriven</groupId>
    <artifactId>jackson-datatype-jts</artifactId>
    <version>2.4</version>
</dependency>

As of 2020 most of the JTS libraries are outdated and no longer work.截至 2020 年,大多数 JTS 库都已过时且不再有效。 I found one fork on Maven Central that was updated recently and it worked flawlessly with jackson-core:2.10.0 and jts-core:1.16.1 :我在 Maven Central 上发现了一个最近更新的 fork,它与jackson-core:2.10.0jts-core:1.16.1完美配合:

implementation 'org.n52.jackson:jackson-datatype-jts:1.2.4'

Sample usage:示例用法:

    @Test
    void testJson() throws IOException {

        var objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JtsModule());

        GeometryFactory gf = new GeometryFactory();
        Point point = gf.createPoint(new Coordinate(1.2345678, 2.3456789));

        String geojson = objectMapper.writeValueAsString(point);

        InputStream targetStream = new ByteArrayInputStream(geojson.getBytes());
        Point point2 = objectMapper.readValue(targetStream, Point.class);

        assertEquals(point, point2);
    }

You don't need to use any annotations on class fields or register new Spring Beans, just register the JTS module with Jackson.您不需要在类字段上使用任何注解或注册新的 Spring Bean,只需向 Jackson 注册 JTS 模块即可。

The above workaround using JTSModule results in an internal SpringBoot error for me.使用 JTSModule 的上述解决方法会导致我的内部 SpringBoot 错误。 I was able to solve this issue by making sure the getter methods of my Entity are returning String types.我能够通过确保我的实体的 getter 方法返回 String 类型来解决这个问题。

@Entity
public class MyClassWithGeom {

    @Id
    @GeneratedValue
    private Long id;
    private Point centre;
    private Polygon boundary;

    private MyClassWithGeom() {}

    public MyClassWithGeom(String centreLat, String centreLng, Double[]... boundaryCoords) {
        String wkt = "POINT (" + centreLat + " " + centreLng + ")";
        StringBuilder builder = new StringBuilder("POLYGON (( ");

        for(int i=0;i<boundaryCoords.length;i++) {
            Double[] coord = boundaryCoords[i];
            if (i < boundaryCoords.length - 1)
                builder = builder.append(coord[0]).append(" ").append(coord[1]).append(", ");
            else
                builder = builder.append(coord[0]).append(" ").append(coord[1]).append(" ))");
        }

        try {
            this.centre = (Point) this.wktToGeometry(wkt);
            logger.info(this.centre.toString());
            this.boundary = (Polygon) this.wktToGeometry(builder.toString());
            logger.info(this.boundary.toString());
        }
        catch (ParseException pe) {
            logger.error(pe.getMessage());
            logger.error("Invalid WKT: " + wkt);
        }
    }

    public Geometry wktToGeometry(String wellKnownText) throws ParseException {
        return new WKTReader().read(wellKnownText);
    }

    public String getCentre() { return centre.toString(); }

    public String getName() { return name; }

    public String getBoundary() { return boundary.toString(); }
}

When I'm dealing with spring boot spatial data types in spring boot, com.vividsolutions.jts.geom.Point raised a lot of issues for me.当我在 spring boot 中处理 spring boot 空间数据类型时, com.vividsolutions.jts.geom.Point为我提出了很多问题。 Currently, I'm using Point of type目前,我正在使用 Point of type

 org.locationtech.jts.geom.Point

which works like a charm这就像一个魅力

Please try to make changes as below and try again..请尝试进行如下更改,然后重试..

  <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>

Change the model like below.更改模型如下。

@Entity
@Data
public class MyEntity{
    // ...
    @Column(columnDefinition = "geometry")
    @JsonDeserialize(as = Point.class)
    private Point location;
    // ...
}

In Case aboveconfiguration does not work with your JacksonSerializer class, please try below once.如果上述配置不适用于您的 JacksonSerializer 类,请尝试以下一次。

public class JacksonSerializer {

    private JacksonSerializer(){

    }

    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static boolean isInit = false;

    
    private static void init() {
        if (isInit == false) {
            objectMapper.setDefaultPropertyInclusion(Include.NON_EMPTY);
            objectMapper.disable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
            objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
            objectMapper.registerModule(new JavaTimeModule());
            objectMapper.setDateFormat(new ISO8601DateFormat());
            objectMapper.setAnnotationIntrospector(new JsonIgnoreIntrospector());


            isInit = true;
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM