JsonMappingException:没有为类型找到合适的构造函数 - 用于外部对象 [英] JsonMappingException: No suitable constructor found for type -- for an external object

查看:143
本文介绍了JsonMappingException:没有为类型找到合适的构造函数 - 用于外部对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个来自spring框架的名为 GeoJsonPoint 的对象,并且在我的集成测试中它无法通过jackson mapper进行反序列化。另外,我无法添加虚拟构造函数,因为它是一个外部对象。所以我被困住了。这是我的主要实体;

  @Document(collection =foodTrucks)
@JsonSerialize(include = JsonSerialize。 Inclusion.NON_EMPTY)
public class FoodTruckEntity {

@Id
private ObjectId id;

私人字符串申请人;
private状态;
private String [] foodItems;
私人经度;
私人双纬;
private GeoJsonPoint geoJsonPoint;

public FoodTruckEntity(){};

// getter and setters
}

并且测试

  @Test 
public void test(){
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures()。put(JSONConfiguration.FEATURE_POJO_MAPPING,Boolean.TRUE);
客户端客户端= Client.create(clientConfig);

String getNearFoodTrucksUrl =http:// localhost:8080 / food-truck / near-locations / longitude / -122.398658184604 / latitude / 37.7901490737255 / findAll;
WebResource webResource = client.resource(getNearFoodTrucksUrl);
ClientResponse response = webResource.get(ClientResponse.class);
GeoResults< FoodTruckEntity> geoResults = webResource.get(new GenericType< GeoResults< FoodTruckEntity>>(){});

if(response.getStatus()!= 200){
抛出新的WebApplicationException();
}
}

我得到的错误;

  com.sun.jersey.api.client.ClientHandlerException:org.codehaus.jackson.map.JsonMappingException:找不到类型为[simple type]的合适构造函数,类org.springframework.data.geo.GeoResults< entity.FoodTruckEntity>]:无法从JSON对象实例化(需要添加/启用类型信息?)
at [来源:sun.net.www.protocol。 http.HttpURLConnection$HttpInputStream@107ed6fc; line:1,column:2]
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:644)
at com.sun.jersey.api.client.ClientResponse。 getEntity(ClientResponse.java:604)

编辑:这是我所拥有的球衣的依赖

 <! -  JERSEY JSON  - > 
< dependency>
< groupId> com.sun.jersey< / groupId>
< artifactId> jersey-client< / artifactId>
< version> 1.18.1< / version>
< / dependency>

<依赖>
< groupId> com.sun.jersey< / groupId>
< artifactId> jersey-json< / artifactId>
< version> 1.18.1< / version>
< / dependency>
<! - JERSEY JSON - >

EDIT_2:作为字符串的响应如下所示,

  {
averageDistance:{
value:0.0,
metric:MILES,
normalizedValue:0.0,
unit:mi
},
content:[
{
content:{
id:{
timestamp:1429498845,
machineIdentifier:11487078,
processIdentifier:1432,
counter:9275496,
time :1429498845000,
date:1429498845000,
timeSecond:1429498845
},
申请人:Cupkates Bakery,LLC,
facilityType :卡车,
状态:批准,
foodItems:[
纸杯蛋糕
],
经度: - 122.398658184604 ,
纬度:37.7901490737255,
geoJso nPoint:{
x: - 122.398658184604,
y:37.7901490737255,
type:Point,
coordinates:[
-122.398658184604,
37.7901490737255
]
}
},
距离:{
value:0.0,
metric :MILES,
normalizedValue:0.0,
unit:mi
}
}
]
}


解决方案

所以如果你看一下 org。 springframework.data.geo ,您会注意到几乎所有类都没有no-arg构造函数,这是 ObjectMapper <的默认行为/ code>需要从JSON反序列化POJO。



使用第三方API解决此问题的一种方法是使用 Jackson Mixins 。如果你看一下 GeoModule ,这是一个可以用 ObjectMapper 注册的模块,包括一些Mixins

  mapper.registerModule(new GeoModule()); 

如果你看一下 org.springframework.data.mongodb.core.geo ,您将另一个模块 GeoJsonModule 也有一些包括Mixins。这个模块应该处理 GeoJsonPoint



但是你的用例的主要问题是(如果你回顾 GeoModule ), GeoResult GeoResults <没有Mixin / code>,你需要解析JSON。



我制作了一个模块来处理 GeoResult GeoResults 目前无效。

 公共类GeoModuleExt扩展SimpleModule {

public GeoModuleExt(){
super(Mixins,new Version(1,0,0,null));
setMixInAnnotation(GeoResult.class,GeoResultMixin.class);
setMixInAnnotation(GeoResults.class,GeoResultsMixin.class);
}

静态抽象类GeoResultMixin {
GeoResultMixin(@JsonProperty(content)对象内容,
@JsonProperty(距离)距离距离){
}
}

静态抽象类GeoResultsMixin {
GeoResultsMixin(@JsonProperty(results)List< GeoResult> results){

}
}
}

你可以玩它。我现在没有时间来处理它(因此是一半 - @ $

解决方案

),但是当我得到一些时间,如果你还没弄清楚,我会看到我能做什么。



作为测试,您可以在独立版中使用 ObjectMapper ,以确保它首先运行

  public class Test {

public static void main(String [] args)throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new GeoJsonModule());
mapper.registerModule(new GeoModule());
//我们的自定义模块
mapper.registerModule(new GeoModuleExt());

//创建FoodTruckEntity -
GeoJsonPoint geoJsonPoint = new GeoJsonPoint(10,10);
FoodTruckEntity foodTruck = new FoodTruckEntity();
//设置foodTruck的所有属性

//创建GeoResult
GeoResult< FoodTruckEntity> geoResult
= new GeoResult< FoodTruckEntity>(foodTruck,new Distance(10,
Metrics.KILOMETERS));

//序列化geoResult
字符串geoResultString = mapper.writeValueAsString(geoResult);
System.out.println(geoResultString);

JavaType type = mapper.getTypeFactory()。constructParametricType(
GeoResult.class,FoodTruckEntity.class);

//反序列化geoResultString
GeoResult< FoodTruckEntity> parsedGeoResult
= mapper.readValue(geoResultString,type);
System.out.println(parsedGeoResult.getDistance());
System.out.println(parsedGeoResult.getContent()。getApplicant());

//到目前为止一切都很好。这是
//`GeoResults`的反序列化,这是一个问题。

/ *
列表< GeoResult> results = new ArrayList< GeoResult>();
results.add(geoResult);
results.add(geoResult);
GeoResults geoResults = new GeoResults(results);

String resultsString = mapper.writeValueAsString(geoResults);
System.out.println(resultsString);


JavaType resultType = mapper.getTypeFactory()。constructParametricType(
GeoResults.class,FoodTruckEntity.class);

GeoResults< FoodTruckEntity> parsedGeoResults
= mapper.readValue(resultsString,resultType);
for(GeoResult< FoodTruckEntity> gr:parsedGeoResults){
System.out.println(gr.getContent()。getGeoJsonPoint());
} * /
}
}

当你得到测试工作,你可以注册 ObjectMapper 与泽西一样

  ObjectMapper mapper = new ObjectMapper(); 
mapper.registerModule(new GeoJsonModule());
mapper.registerModule(new GeoModule());
//我们的自定义模块
mapper.registerModule(new GeoModuleExt());

ClientConfig config = new DefaultClientConfig();
config.getSingletons()。add(new JacksonJsonProvider(mapper));
客户端客户端= Client.create(config);






更新



因此,经过一些游戏,我能够使用这个Mixin来处理 GeoResults 。只需更新上面的 GeoModuleExt

 静态抽象类GeoResultsMixin {
GeoResultsMixin(@JsonProperty(results)List< GeoResult>结果,
@JsonProperty(averageDistance)距离averageDistance){
}

@JsonProperty(结果 )
abstract List< GeoResult>的getContent();
}

通过上述测试,它的工作正常。尚未使用Jersey进行过测试,但是如果它与 ObjectMapper 一起使用,只要我们配置Jackson提供程序使用它就不应该是Jersey的问题映射器。


I have an object called GeoJsonPoint from spring framework, and it can't get deserialized by jackson mapper in my integration test. Additionally, I can't add a dummy constructor because it is an external object. So I am stuck. This is my main entity;

@Document(collection = "foodTrucks")
@JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY)
public class FoodTruckEntity {

    @Id
    private ObjectId id;

    private String applicant;
    private Status status;
    private String[] foodItems;
    private Double longitude;
    private Double latitude;
    private GeoJsonPoint geoJsonPoint;

    public FoodTruckEntity() {};

    // getters and setters
}

And the test

@Test
public void test() {
    ClientConfig clientConfig = new DefaultClientConfig();
    clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
    Client client = Client.create(clientConfig);

    String getNearFoodTrucksUrl = "http://localhost:8080/food-truck/near-locations/longitude/-122.398658184604/latitude/37.7901490737255/findAll";
    WebResource webResource = client.resource(getNearFoodTrucksUrl);
    ClientResponse response = webResource.get(ClientResponse.class);
    GeoResults<FoodTruckEntity> geoResults = webResource.get(new GenericType<GeoResults<FoodTruckEntity>>(){});

    if (response.getStatus() != 200) {
        throw new WebApplicationException();
    }
}

And the error I get;

com.sun.jersey.api.client.ClientHandlerException: org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class org.springframework.data.geo.GeoResults<entity.FoodTruckEntity>]: can not instantiate from JSON object (need to add/enable type information?)
 at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@107ed6fc; line: 1, column: 2]
    at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:644)
    at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:604)

EDIT: This is the dependencies for jersey I have

<!-- JERSEY JSON -->
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-client</artifactId>
    <version>1.18.1</version>
</dependency>

<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-json</artifactId>
    <version>1.18.1</version>
</dependency>
<!-- JERSEY JSON -->

EDIT_2: Response as a String looks like this,

{
   "averageDistance":{
      "value":0.0,
      "metric":"MILES",
      "normalizedValue":0.0,
      "unit":"mi"
   },
   "content":[
      {
         "content":{
            "id":{
               "timestamp":1429498845,
               "machineIdentifier":11487078,
               "processIdentifier":1432,
               "counter":9275496,
               "time":1429498845000,
               "date":1429498845000,
               "timeSecond":1429498845
            },
            "applicant":"Cupkates Bakery, LLC",
            "facilityType":"Truck",
            "status":"APPROVED",
            "foodItems":[
               "Cupcakes"
            ],
            "longitude":-122.398658184604,
            "latitude":37.7901490737255,
            "geoJsonPoint":{
               "x":-122.398658184604,
               "y":37.7901490737255,
               "type":"Point",
               "coordinates":[
                  -122.398658184604,
                  37.7901490737255
               ]
            }
         },
         "distance":{
            "value":0.0,
            "metric":"MILES",
            "normalizedValue":0.0,
            "unit":"mi"
         }
      }
   ]
}

解决方案

So if you look at all the classes from the org.springframework.data.geo, you will notice that pretty much all the classes don't have a no-arg constructor, which the default behavior of the ObjectMapper needs to deserialize POJOs from JSON.

One way to get around this with third-party APIs, is to make use of Jackson Mixins. If you look at the GeoModule, this is a module you can register with the ObjectMapper, that includes some Mixins

mapper.registerModule(new GeoModule());

If you look at the org.springframework.data.mongodb.core.geo, you will another module GeoJsonModule that has some included Mixins also. This module should take care of the GeoJsonPoint.

But the main problem for your use case, is (if you look back at the GeoModule) is that there is no Mixin for the GeoResult or GeoResults, which you need to parse the JSON.

I made a module to take care of the GeoResult but the GeoResults doesn't work at the moment.

public class GeoModuleExt extends SimpleModule {

    public GeoModuleExt() {
        super("Mixins", new Version(1, 0, 0, null));
        setMixInAnnotation(GeoResult.class, GeoResultMixin.class);
        setMixInAnnotation(GeoResults.class, GeoResultsMixin.class);
    }

    static abstract class GeoResultMixin {
        GeoResultMixin(@JsonProperty("content") Object content, 
                       @JsonProperty("distance") Distance distance) {    
        }
    }

    static abstract class GeoResultsMixin {
        GeoResultsMixin(@JsonProperty("results")List<GeoResult> results) {

        }   
    }
}

You can play around with it. I don't have time right now to work on it (hence the half-@$$ solution), but when I get some time, if you haven't figured it out, I'll see what I can do.

As a test, you can use the ObjectMapper in a standalone, to make sure it works first

public class Test {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new GeoJsonModule());
        mapper.registerModule(new GeoModule());
        // Our custom module
        mapper.registerModule(new GeoModuleExt());

        // Create FoodTruckEntity - 
        GeoJsonPoint geoJsonPoint = new GeoJsonPoint(10, 10);
        FoodTruckEntity foodTruck = new FoodTruckEntity();
        // set all properties fro foodTruck

        // Create GeoResult
        GeoResult<FoodTruckEntity> geoResult
                = new GeoResult<FoodTruckEntity>(foodTruck, new Distance(10,
                                                 Metrics.KILOMETERS));

        // Serialize geoResult
        String geoResultString = mapper.writeValueAsString(geoResult);
        System.out.println(geoResultString);

        JavaType type = mapper.getTypeFactory().constructParametricType(
                GeoResult.class, FoodTruckEntity.class);

        // Deserialize geoResultString
        GeoResult<FoodTruckEntity> parsedGeoResult 
                = mapper.readValue(geoResultString, type);
        System.out.println(parsedGeoResult.getDistance());
        System.out.println(parsedGeoResult.getContent().getApplicant());

        // Up to this point everything is fine. It's the deserialization of
        // `GeoResults` thats a problem.

        /*
        List<GeoResult> results = new ArrayList<GeoResult>();
        results.add(geoResult);
        results.add(geoResult);
        GeoResults geoResults = new GeoResults(results);

        String resultsString = mapper.writeValueAsString(geoResults);
        System.out.println(resultsString);


        JavaType resultType = mapper.getTypeFactory().constructParametricType(
                                       GeoResults.class, FoodTruckEntity.class);

        GeoResults<FoodTruckEntity> parsedGeoResults 
                            = mapper.readValue(resultsString, resultType);
        for (GeoResult<FoodTruckEntity> gr: parsedGeoResults) {
            System.out.println(gr.getContent().getGeoJsonPoint());
        }*/
    }
}

When you get the test to work, you can register the ObjectMapper with Jersey like

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new GeoJsonModule());
mapper.registerModule(new GeoModule());
// Our custom module
mapper.registerModule(new GeoModuleExt());

ClientConfig config = new DefaultClientConfig();
config.getSingletons().add(new JacksonJsonProvider(mapper));
Client client = Client.create(config);


UPDATE

So after some playing around, I was able to get it to work with this Mixin for the GeoResults. Just update the above GeoModuleExt

static abstract class GeoResultsMixin {
    GeoResultsMixin(@JsonProperty("results") List<GeoResult> results, 
                    @JsonProperty("averageDistance") Distance averageDistance) {
    }

    @JsonProperty("results")
    abstract List<GeoResult> getContent(); 
}

It works as expected with the above test. Haven't tested yet with Jersey, but if it works with the ObjectMapper, it shouldn't be a problem with Jersey, as long as we have configured the Jackson provider to use the mapper.

这篇关于JsonMappingException:没有为类型找到合适的构造函数 - 用于外部对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆