Java Jackson无法创建已在ObjectMapper中注册的类的实例
我有一个父对象和一个子对象。父对象可能多次包含相同的子对象,因此我仅序列化子对象一次,下一个实例仅由其ID引用。当我从子对象中删除@JsonIdentityInfo注释时,该对象将无错误地反序列化。对我来说,这感觉像是一个Jackson bug,但也许有人发现了我代码中的错误。我举了一个小例子来说明错误(使用的Jackson版本是2.9.4): 父类:Java Jackson无法创建已在ObjectMapper中注册的类的实例,java,json,jackson,json-deserialization,Java,Json,Jackson,Json Deserialization,我有一个父对象和一个子对象。父对象可能多次包含相同的子对象,因此我仅序列化子对象一次,下一个实例仅由其ID引用。当我从子对象中删除@JsonIdentityInfo注释时,该对象将无错误地反序列化。对我来说,这感觉像是一个Jackson bug,但也许有人发现了我代码中的错误。我举了一个小例子来说明错误(使用的Jackson版本是2.9.4): 父类: import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.util.A
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import java.util.ArrayList;
import java.util.Collection;
import lombok.Getter;
import lombok.Setter;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,
property = "type", defaultImpl = TestClassParent.class)
public class TestClassParent {
@Getter
@Setter
private ITestClassChild child1;
@Getter
@Setter
private Collection<ITestClassChild> children;
public TestClassParent(){
child1 = new TestClassChild();
children = new ArrayList<>();
children.add(child1);
}
}
界面:
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,
property = "type", defaultImpl = ITestClassChild.class)
public interface ITestClassChild {
public String getId();
}
测试用例:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import org.junit.Test;
public class TestClassImportExportTest{
@Test
public void test() throws JsonProcessingException, IOException{
ObjectMapper om = new ObjectMapper();
om.registerSubtypes(
TestClassChild.class
);
TestClassParent original = new TestClassParent();
String json = om.writerWithDefaultPrettyPrinter().writeValueAsString(original);
TestClassParent imported = om.readValue(json, TestClassParent.class);
}
}
在以下错误中执行测试结果:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `json.ITestClassChild` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (String)"{
"type" : "TestClassParent",
"child1" : {
"type" : "TestClassChild",
"id" : "1"
},
"children" : [ "1" ]
}"; line: 7, column: 18] (through reference chain: json.TestClassParent["children"]->java.util.ArrayList[0])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1451)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1027)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:265)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedUsingDefaultImpl(AsPropertyTypeDeserializer.java:178)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:88)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:254)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:189)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:130)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:97)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:1171)
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)
at json.TestClassImportExportTest.test(TestClassImportExportTest.java:18)
如上所述,从TestClassChild中删除@JsonIdentityInfo注释会使测试用例顺利通过。但是,我需要注释以获得正确的导入。该错误似乎是由使用collection+interface+id引起的,删除其中任何一个似乎都可以,我很高兴能得到任何帮助 Jackson无法构造
ITestClassChild
的对象,因为它是一个接口,与JsonIdentityInfo
一样,它试图保持双向关系
您可以在此处阅读JsonIdentityInfo
-
例如,With接口需要使用@JsonSubTypes
指定要使用的类型-
@JsonSubTypes({@Type(value=TestClassChild.class,name=“child1”)})
公共接口ITestClassChild
请参阅此以获得更好的解释
二手-杰克逊2.8。下面是我的代码,我仍然使用输入得到相同的异常
{
“类型”:“TestClassParent”,
“儿童”:{
“类型”:“TestClassChild”,
“id”:“1”
}
}
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.PROPERTY,
property=“type”,defaultImpl=TestClassParent.class)
类TestClassParent{
私家儿童;
/*私人收藏儿童*/
公共TestClassParent(){
//child=newtestclasschild();
/*children=newarraylist();
添加(child1)*/
}
公共ITestClassChild getChild(){
返回儿童;
}
公共void setChild(ITestClassChild){
这个孩子=孩子;
}
}
@JsonTypeInfo(使用=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.PROPERTY,
property=“type”,defaultImpl=ITestClassChild.class)
接口ITestClassChild{
公共字符串getId();
}
@JsonTypeInfo(使用=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.PROPERTY,
property=“type”,defaultImpl=TestClassChild.class)
@JsonIdentityInfo(生成器=ObjectedGenerators.PropertyGenerator.class,
property=“id”)
类TestClassChild实现ITestClassChild{
私有字符串id;
公共TestClassChild(){
id=“1”;
}
@凌驾
公共字符串getId(){
返回id;
}
}
但是,如果按如下方式使用输入,则不会得到异常,因为它会忽略属性
{
“类型”:“TestClassParent”,
}
因此,以下解决方法应该有效:
我为每个子类添加了一个自定义反序列化程序(使用@jsondeselliate(使用=TestClassChildDeserializer.class)注释该类),并为接口添加了一个自定义反序列化程序(也以相同的方式注释)。然后我反序列化对象(也许有办法使用默认的反序列化器?),并将对象添加到接口反序列化器中的静态哈希映射中
反序列化程序只不过获取节点的textValue并在HashMap中返回该节点的值。子反序列化程序反序列化对象并将其保存到HashMap中
它非常难看,但目前仍然有效(尽管我需要在反序列化后清空HashMap,而且它也不是线程安全的…)
我也尝试了这里提出的解决方案:没有用。谢谢。我已经在对象映射器中注册了子类型,它应该可以很好地工作,而且确实可以。当我从TestClassParent(和构造函数)中删除Collection属性时,该类被正确序列化和反序列化,即使它包含另一个ITestClassChild属性(child1)。这意味着Jackson可以正确地处理接口,只是当它是集合的泛型类型时就不行了。我也尝试过你的建议,在接口上使用@JsonSubTypes(),但我仍然得到了相同的错误。我认为它仍然给出了例外。。请参考以上我的评论。谢谢你的帮助。
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `json.ITestClassChild` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (String)"{
"type" : "TestClassParent",
"child1" : {
"type" : "TestClassChild",
"id" : "1"
},
"children" : [ "1" ]
}"; line: 7, column: 18] (through reference chain: json.TestClassParent["children"]->java.util.ArrayList[0])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1451)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1027)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:265)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedUsingDefaultImpl(AsPropertyTypeDeserializer.java:178)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:88)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:254)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:189)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:130)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:97)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:1171)
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)
at json.TestClassImportExportTest.test(TestClassImportExportTest.java:18)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,
property = "type", defaultImpl = TestClassParent.class)
class TestClassParent {
private ITestClassChild child;
/*private Collection<ITestClassChild> children;*/
public TestClassParent(){
//child = new TestClassChild();
/*children = new ArrayList<>();
children.add(child1);*/
}
public ITestClassChild getChild() {
return child;
}
public void setChild(ITestClassChild child) {
this.child = child;
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,
property = "type", defaultImpl = ITestClassChild.class)
interface ITestClassChild {
public String getId();
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,
property = "type", defaultImpl = TestClassChild.class)
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,
property="id")
class TestClassChild implements ITestClassChild {
private String id;
public TestClassChild(){
id = "1";
}
@Override
public String getId() {
return id;
}
}