Java 如何使用JsonTypeInfo和反序列化器来定制多态子类型的处理?
在这种情况下,我需要定制一些JSON的序列化/反序列化。我已将其简化为一个可读的示例。我有一个容器类,它保存实现MyInterface的对象。在我的示例中,ClassA、ClassB、IntegerHolder和StringHolder实现了接口。通过将@JsonTypeInfo注释添加到我的接口(和容器): 通过为每个类注册类型名称,我可以成功地从JSON中读取/写入这些内容:Java 如何使用JsonTypeInfo和反序列化器来定制多态子类型的处理?,java,json,polymorphism,jackson,Java,Json,Polymorphism,Jackson,在这种情况下,我需要定制一些JSON的序列化/反序列化。我已将其简化为一个可读的示例。我有一个容器类,它保存实现MyInterface的对象。在我的示例中,ClassA、ClassB、IntegerHolder和StringHolder实现了接口。通过将@JsonTypeInfo注释添加到我的接口(和容器): 通过为每个类注册类型名称,我可以成功地从JSON中读取/写入这些内容: {"type":"Container","items": [ {"type":"classA","aVa
{"type":"Container","items":
[ {"type":"classA","aValue":"AAA"},
{"type":"classB","bValue":"BBB"},
{"type":"intHolder","value":123},
{"type":"stringHolder","value":"abc"} ] }
这一切都很好:)我的问题是,我想自定义intHolder和stringHolder的序列化,因为它们只是本地类型的包装。我的JSON将经常手工编辑,基本类型将被大量使用。因此,我想将JSON简化为:
{"type":"Container","items":
[ {"type":"classA","aValue":"AAA"},
{"type":"classB","bValue":"BBB"},
123,
"abc" ] }
我已经编写了一个序列化程序和反序列化程序(扩展了StdSeralizer和StdDeserializer),将它们放在一个SimpleModule中,并向映射程序注册了它(如图所示),在隔离状态下,它工作得很好。我的意思是,如果IntegerHolder和StringHolder是容器中唯一的对象,并且只有当我从接口中删除@JsonTypeInfo注释时,我才能序列化/反序列化它们。如果没有,则在写入JSON时会出现以下错误:
[main] ERROR MyTests - can't write the Container
com.fasterxml.jackson.databind.JsonMappingException: Type id handling not implemented for type MyInterface (by serializer of type MyTests$MyInterfaceSerializer) (through reference chain: Container["items"])
at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1047)
at com.fasterxml.jackson.databind.JsonSerializer.serializeWithType(JsonSerializer.java:142)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serializeTypedContents(ObjectArraySerializer.java:316)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serializeContents(ObjectArraySerializer.java:217)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serialize(ObjectArraySerializer.java:201)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serialize(ObjectArraySerializer.java:25)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeWithType(BeanSerializerBase.java:552)
at com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer.serialize(TypeWrappedSerializer.java:32)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3387)
at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:2747)
at MyTests.testItemSerializationDeserializationEquality(MyTests.java:51)
at MyTests.testSerialization(MyTests.java:41)
但当然,删除了@JsonTypeInfo后,Jackson不知道如何反序列化ClassA和ClassB…因此在读取JSON时失败,原因是:
[main] INFO MyTests - {"type":"Container","items":[{"aValue":"AAA"},{"bValue":"BBB"},123,"abc"]}
[main] ERROR MyTests - can't read the Container
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of MyInterface, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
at [Source: java.io.ByteArrayInputStream@37883b97; line: 1, column: 45] (through reference chain: Container["items"]->Object[][0])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:857)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:139)
at MyTests$MyInterfaceDeserializer.deserialize(MyTests.java:163)
at MyTests$MyInterfaceDeserializer.deserialize(MyTests.java:139)
我觉得Jackson可以做到这一点,我很快就可以将Jackson配置为序列化/反序列化这两组类,但到目前为止,我的尝试并没有取得成果
任何能让我朝正确方向前进的建议都将不胜感激…提前谢谢
以下是我的测试示例中的7个类:
MyInterface.java
import com.fasterxml.jackson.annotation.*;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public interface MyInterface
{
}
Container.java
import com.fasterxml.jackson.annotation.*;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public class Container
{
public Container()
{
}
public Container(MyInterface... items)
{
this.items = items;
}
public MyInterface[] getItems()
{
return items;
}
public void setItems(MyInterface[] items)
{
this.items = items;
}
@Override
public boolean equals(Object obj)
{
for (int i = 0; i < items.length; i++)
if (!(items[i].equals(((Container)obj).items[i])))
return false;
return true;
}
private MyInterface[] items;
}
ClassB.java
public class ClassB implements MyInterface
{
public String getbValue()
{
return _bValue;
}
public void setbValue(String bValue)
{
_bValue = bValue;
}
@Override
public boolean equals(Object obj)
{
return obj instanceof ClassB && _bValue.equals(((ClassB)obj)._bValue);
}
private String _bValue;
}
StringHolderClass.java
public class StringHolderClass implements MyInterface
{
public String getValue()
{
return _value;
}
public void setValue(String value)
{
_value = value;
}
@Override
public boolean equals(Object obj)
{
return obj instanceof StringHolderClass && _value.equals(((StringHolderClass)obj)._value);
}
private String _value;
}
public class IntegerHolderClass implements MyInterface
{
public int getValue()
{
return _value;
}
public void setValue(int value)
{
_value = value;
}
@Override
public boolean equals(Object obj)
{
return obj instanceof IntegerHolderClass && _value.equals(((IntegerHolderClass)obj)._value);
}
private Integer _value;
}
IntegerHolderClass.java
public class StringHolderClass implements MyInterface
{
public String getValue()
{
return _value;
}
public void setValue(String value)
{
_value = value;
}
@Override
public boolean equals(Object obj)
{
return obj instanceof StringHolderClass && _value.equals(((StringHolderClass)obj)._value);
}
private String _value;
}
public class IntegerHolderClass implements MyInterface
{
public int getValue()
{
return _value;
}
public void setValue(int value)
{
_value = value;
}
@Override
public boolean equals(Object obj)
{
return obj instanceof IntegerHolderClass && _value.equals(((IntegerHolderClass)obj)._value);
}
private Integer _value;
}
两种选择:
MyInterface
定制反序列化程序,然后您不需要JsonTypeInfo-所有逻辑都将在反序列化程序中IntegerHolder
和StringHolder
实现另一个接口,比如说Holder
,并将JsonTypeInfo
注释更改为:
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,property=“type”,defaultImpl=Holder.class)
并为Holder.class指定一个反序列化器public class IntegerHolderClass implements MyInterface
{
public int getValue()
{
return _value;
}
public void setValue(int value)
{
_value = value;
}
@Override
public boolean equals(Object obj)
{
return obj instanceof IntegerHolderClass && _value.equals(((IntegerHolderClass)obj)._value);
}
private Integer _value;
}