JsonMappingException:没有为类型找到合适的构造函数 - 用于外部对象

时间:2015-04-19 23:26:15

标签: java json jersey jackson

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

@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
}

和测试

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

我得到的错误;

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)

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

<!-- 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:作为字符串的响应看起来像这样,

{
   "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"
         }
      }
   ]
}

1 个答案:

答案 0 :(得分:5)

因此,如果您查看org.springframework.data.geo中的所有类,您会注意到几乎所有类都没有没有arg构造函数,这是{{1}的默认行为需要从JSON反序列化POJO。

使用第三方API解决此问题的一种方法是使用Jackson Mixins。如果您查看GeoModule,这是一个可以在ObjectMapper注册的模块,其中包含一些Mixins

ObjectMapper

如果你看一下org.springframework.data.mongodb.core.geo,你会看到另一个模块GeoJsonModule,其中包含一些Mixins。该模块应该处理mapper.registerModule(new GeoModule());

但是您的用例的主要问题是(如果您回顾GeoJsonPoint)是GeoModuleGeoResult没有Mixin,您需要解析JSON。

我制作了一个模块来处理GeoResults,但GeoResult目前还没有工作。

GeoResults

你可以玩它。我现在没有时间去做这件事(因此半价@ $$解决方案),但是当我有时间的时候,如果你还没弄清楚,我会看到什么我能做到。

作为测试,您可以在独立版中使用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) { } } } ,以确保它首先运行

ObjectMapper

当您开始测试时,您可以将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()); }*/ } } 注册为泽西

ObjectMapper

更新

所以经过一些游戏,我能够使用这个Mixin来处理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); 。只需更新上面的GeoResults

即可
GeoModuleExt

通过上述测试,它可以正常工作。 Haven尚未使用Jersey进行测试,但如果它与static abstract class GeoResultsMixin { GeoResultsMixin(@JsonProperty("results") List<GeoResult> results, @JsonProperty("averageDistance") Distance averageDistance) { } @JsonProperty("results") abstract List<GeoResult> getContent(); } 一起使用,只要我们配置Jackson提供程序使用映射器,它就不应该是Jersey的问题。