Jackson 无法创建已在 ObjectMapper 中注册的 class 的实例
Jackson cannot create instance of a class already registered in ObjectMapper
我有一个 parent object 和一个 child object。 parent object 可能多次包含相同的 child object,所以我只序列化 child object 一次,下一个实例是仅由其 ID 引用。当我从 child object 中删除 @JsonIdentityInfo 注释时,object 反序列化没有错误。对我来说,这感觉像是 Jackson 的错误,但也许有人在我的代码中发现了错误。我做了一个显示错误的小例子(使用的 Jackson 版本是 2.9.4):
parent class:
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);
}
}
child class:
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import lombok.Getter;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,
property = "type", defaultImpl = TestClassChild.class)
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,
property="id")
public class TestClassChild implements ITestClassChild{
@Getter
private String id;
public TestClassChild(){
id = "1";
}
}
界面:
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
- Jackson Relationships
with interface需要指定使用@JsonSubTypes
的Type,例如-
@JsonSubTypes({ @Type(value = TestClassChild.class, name = "child1")})
public interface ITestClassChild
参考此 blog 以获得更好的解释。
二手 - 杰克逊 2.8。下面是我的代码我仍然使用输入
得到相同的异常
{
"type" : "TestClassParent",
"child" : {
"type" : "TestClassChild",
"id" : "1"
}
}
@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;
}
}
但是如果你使用下面的输入你不会得到异常因为它忽略了 属性.
{
"type" : "TestClassParent",
}
因此,以下解决方法应该有效:
我为每个子 class 添加了一个自定义反序列化器(用 @JsonDeserialize(using = TestClassChildDeserializer.class) 注释 class )和一个接口(也注释了同样的方式)。然后我反序列化对象(也许有一种方法可以使用默认的反序列化器?)并将对象添加到接口反序列化器中的静态哈希图中。
反序列化器只是获取节点的textValue 并返回该节点的HashMap 中的值。子反序列化器反序列化对象并将其保存到 HashMap 中。
它非常丑陋,但暂时有效(虽然我需要在反序列化后清空 HashMap,而且它也不是线程安全的...)。
我也尝试了这里提出的解决方案:https://github.com/FasterXML/jackson-databind/issues/1641 无济于事。
我有一个 parent object 和一个 child object。 parent object 可能多次包含相同的 child object,所以我只序列化 child object 一次,下一个实例是仅由其 ID 引用。当我从 child object 中删除 @JsonIdentityInfo 注释时,object 反序列化没有错误。对我来说,这感觉像是 Jackson 的错误,但也许有人在我的代码中发现了错误。我做了一个显示错误的小例子(使用的 Jackson 版本是 2.9.4): parent class:
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);
}
}
child class:
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import lombok.Getter;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,
property = "type", defaultImpl = TestClassChild.class)
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,
property="id")
public class TestClassChild implements ITestClassChild{
@Getter
private String id;
public TestClassChild(){
id = "1";
}
}
界面:
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
- Jackson Relationships
with interface需要指定使用@JsonSubTypes
的Type,例如-
@JsonSubTypes({ @Type(value = TestClassChild.class, name = "child1")})
public interface ITestClassChild
参考此 blog 以获得更好的解释。
二手 - 杰克逊 2.8。下面是我的代码我仍然使用输入
得到相同的异常{
"type" : "TestClassParent",
"child" : {
"type" : "TestClassChild",
"id" : "1"
}
}
@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;
}
}
但是如果你使用下面的输入你不会得到异常因为它忽略了 属性.
{
"type" : "TestClassParent",
}
因此,以下解决方法应该有效:
我为每个子 class 添加了一个自定义反序列化器(用 @JsonDeserialize(using = TestClassChildDeserializer.class) 注释 class )和一个接口(也注释了同样的方式)。然后我反序列化对象(也许有一种方法可以使用默认的反序列化器?)并将对象添加到接口反序列化器中的静态哈希图中。
反序列化器只是获取节点的textValue 并返回该节点的HashMap 中的值。子反序列化器反序列化对象并将其保存到 HashMap 中。
它非常丑陋,但暂时有效(虽然我需要在反序列化后清空 HashMap,而且它也不是线程安全的...)。
我也尝试了这里提出的解决方案:https://github.com/FasterXML/jackson-databind/issues/1641 无济于事。