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状态;
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屋!