首页 文章

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

提问于
浏览
6

我有一个名为 GeoJsonPoint 的对象来自spring框架,它可以't get deserialized by jackson mapper in my integration test. Additionally, I can'添加一个虚拟构造函数,因为它是一个外部对象 . 所以我被困住了 . 这是我的主要实体;

@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 回答

  • 5

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

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

    mapper.registerModule(new GeoModule());
    

    如果你看一下org.springframework.data.mongodb.core.geo,你会看到另一个包含Mixins的模块GeoJsonModule . 该模块应该处理 GeoJsonPoint .

    但是你的用例的主要问题是(如果你回头看 GeoModule )是 GeoResultGeoResults 没有Mixin,你需要解析JSON .

    我制作了一个模块来处理 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

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

    更新

    所以经过一些游戏后,我能够使用这个Mixin来处理 GeoResults . 只需更新上面的 GeoModuleExt

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

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

相关问题