JsonMappingException:找不到适合类型的构造函数 -- 对于外部对象
JsonMappingException: No suitable constructor found for type -- for an external object
我有一个来自 spring 框架的名为 GeoJsonPoint
的对象,在我的集成测试中它无法被 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"
}
}
]
}
因此,如果您查看 org.springframework.data.geo
中的所有 类,您会注意到几乎所有 类 都没有无参数构造函数, ObjectMapper
的默认行为需要从 JSON.
反序列化 POJO
使用第三方 API 解决此问题的一种方法是使用 Jackson Mixins. If you look at the GeoModule
,这是一个您可以在 ObjectMapper
注册的模块,其中包含一些 Mixins
mapper.registerModule(new GeoModule());
如果您查看 org.springframework.data.mongodb.core.geo
, you will another module GeoJsonModule
,其中也包含一些 Mixins。该模块应处理 GeoJsonPoint
.
但是您的用例的主要问题是(如果您回顾 GeoModule
)是 GeoResult
或 GeoResults
没有 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) {
}
}
}
你可以玩玩它。我现在没有时间处理它(因此是 half-@$$ 解决方案),但是当我有时间的时候,如果你还没有弄清楚,我会看看我能做些什么。
作为测试,您可以单独使用 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());
}*/
}
}
当你让测试工作时,你可以像
一样用 Jersey 注册 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);
更新
所以经过一番尝试,我能够让它与 GeoResults
的这个 Mixin 一起工作。只需更新以上 GeoModuleExt
static abstract class GeoResultsMixin {
GeoResultsMixin(@JsonProperty("results") List<GeoResult> results,
@JsonProperty("averageDistance") Distance averageDistance) {
}
@JsonProperty("results")
abstract List<GeoResult> getContent();
}
上面的测试按预期工作。尚未对 Jersey 进行测试,但如果它与 ObjectMapper
一起工作,那么 Jersey 应该不是问题,只要我们已将 Jackson 提供程序配置为使用映射器。
我有一个来自 spring 框架的名为 GeoJsonPoint
的对象,在我的集成测试中它无法被 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"
}
}
]
}
因此,如果您查看 org.springframework.data.geo
中的所有 类,您会注意到几乎所有 类 都没有无参数构造函数, ObjectMapper
的默认行为需要从 JSON.
使用第三方 API 解决此问题的一种方法是使用 Jackson Mixins. If you look at the GeoModule
,这是一个您可以在 ObjectMapper
注册的模块,其中包含一些 Mixins
mapper.registerModule(new GeoModule());
如果您查看 org.springframework.data.mongodb.core.geo
, you will another module GeoJsonModule
,其中也包含一些 Mixins。该模块应处理 GeoJsonPoint
.
但是您的用例的主要问题是(如果您回顾 GeoModule
)是 GeoResult
或 GeoResults
没有 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) {
}
}
}
你可以玩玩它。我现在没有时间处理它(因此是 half-@$$ 解决方案),但是当我有时间的时候,如果你还没有弄清楚,我会看看我能做些什么。
作为测试,您可以单独使用 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());
}*/
}
}
当你让测试工作时,你可以像
一样用 Jersey 注册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);
更新
所以经过一番尝试,我能够让它与 GeoResults
的这个 Mixin 一起工作。只需更新以上 GeoModuleExt
static abstract class GeoResultsMixin {
GeoResultsMixin(@JsonProperty("results") List<GeoResult> results,
@JsonProperty("averageDistance") Distance averageDistance) {
}
@JsonProperty("results")
abstract List<GeoResult> getContent();
}
上面的测试按预期工作。尚未对 Jersey 进行测试,但如果它与 ObjectMapper
一起工作,那么 Jersey 应该不是问题,只要我们已将 Jackson 提供程序配置为使用映射器。