我有一组简单的类,我想使用Jackson(2.4.5)从YAML序列化/反序列化:
public static class Animal {
public String name;
}
public static class Dog extends Animal {
public String breed;
}
public static class Cat extends Animal {
public String favoriteToy;
}
public static class AnimalRegistry {
private Map<String, Animal> fAnimals = new HashMap<>();
public AnimalRegistry(Animal... animals) {
for (Animal animal : animals)
fAnimals.put(animal.name, animal);
}
public Animal getAnimal(String name) {
return fAnimals.get(name);
}
}
这样做非常简单,以便AnimalRegistry
最终成为Animal
(子类型)对象的嵌套对象列表。我可以写和读那些就好了。我面临的问题是单独序列化/反序列化另一个类的对象:
public static class PetOwner {
public String name;
public List<Animal> pets = new ArrayList<>();
}
我不希望将Animal
对象序列化为嵌套对象列表,而只是存储Animal
个名称的列表。在反序列化时,我想使用预先存在的Animal
将这些名称映射回AnimalRegistry
个对象。
使用JAXB,我可以使用XmlAdapter
:
public static class PetXmlAdapter extends XmlAdapter<String, Animal> {
private AnimalRegistry fRegistry;
public PetXmlAdapter(AnimalRegistry registry) {
fRegistry = registry;
}
@Override
public Animal unmarshal(String value) throws Exception {
return fRegistry.getAnimal(value);
}
@Override
public String marshal(Animal value) throws Exception {
return value.name;
}
}
我用
注释pets
字段
@XmlJavaTypeAdapter(value = PetXmlAdapter.class)
并将PetXmlAdapter
的实例添加到Marshaller
/ Unmarshaller
:
marshaller.setAdapter(new PetXmlAdapter(animalRegistry));
...
unmarshaller.setAdapter(new PetXmlAdapter(animalRegistry));
Jackson支持JAXB注释并且可以使用相同的PetXmlAdapter
类,但是我没有看到在ObjectMapper
或任何相关类上设置它的实例的方法,因此不能使用我之前存在的AnimalRegistry
。
杰克逊似乎有很多定制点,最后我找到了实现目标的方法:
public static class AnimalNameConverter
extends StdConverter<Animal, String> {
@Override
public String convert(Animal value) {
return value != null ? value.name : null;
}
}
public static class NameAnimalConverter
extends StdConverter<String, Animal> {
private AnimalRegistry fRegistry;
public NameAnimalConverter(AnimalRegistry registry) {
fRegistry = registry;
}
@Override
public Animal convert(String value) {
return value != null ? fRegistry.getAnimal(value) : null;
}
}
public static class AnimalSerializer
extends StdDelegatingSerializer {
public AnimalSerializer() {
super(Animal.class, new AnimalNameConverter());
}
private AnimalSerializer(Converter<Object,?> converter,
JavaType delegateType,
JsonSerializer<?> delegateSerializer) {
super(converter, delegateType, delegateSerializer);
}
@Override
protected StdDelegatingSerializer withDelegate(
Converter<Object, ?> converter, JavaType delegateType,
JsonSerializer<?> delegateSerializer) {
return new AnimalSerializer(converter, delegateType,
delegateSerializer);
}
}
public static class AnimalDeserializer
extends StdDelegatingDeserializer<Animal> {
private static final long serialVersionUID = 1L;
public AnimalDeserializer(AnimalRegistry registry) {
super(new NameAnimalConverter(registry));
}
private AnimalDeserializer(Converter<Object, Animal> converter,
JavaType delegateType,
JsonDeserializer<?> delegateDeserializer) {
super(converter, delegateType, delegateDeserializer);
}
@Override
protected StdDelegatingDeserializer<Animal> withDelegate(
Converter<Object, Animal> converter,
JavaType delegateType,
JsonDeserializer<?> delegateDeserializer) {
return new AnimalDeserializer(converter, delegateType,
delegateDeserializer);
}
}
public static class AnimalHandlerInstantiator
extends HandlerInstantiator {
private AnimalRegistry fRegistry;
public AnimalHandlerInstantiator(AnimalRegistry registry) {
fRegistry = registry;
}
@Override
public JsonDeserializer<?> deserializerInstance(
DeserializationConfig config, Annotated annotated,
Class<?> deserClass) {
if (deserClass != AnimalDeserializer.class)
return null;
return new AnimalDeserializer(fRegistry);
}
@Override
public KeyDeserializer keyDeserializerInstance(
DeserializationConfig config, Annotated annotated,
Class<?> keyDeserClass) {
return null;
}
@Override
public JsonSerializer<?> serializerInstance(
SerializationConfig config, Annotated annotated,
Class<?> serClass) {
return null;
}
@Override
public TypeResolverBuilder<?> typeResolverBuilderInstance(
MapperConfig<?> config, Annotated annotated,
Class<?> builderClass) {
return null;
}
@Override
public TypeIdResolver typeIdResolverInstance(
MapperConfig<?> config,
Annotated annotated, Class<?> resolverClass) {
return null;
}
}
我用
注释pets
字段
@JsonSerialize(contentUsing = AnimalSerializer.class)
@JsonDeserialize(contentUsing = AnimalDeserializer.class)
并在AnimalHandlerInstantiator
上设置ObjectMapper
的实例:
mapper.setHandlerInstantiator(
new AnimalHandlerInstantiator(animalRegistry));
这很有效,但是代码很多。任何人都可以提出更简洁的选择吗?我想避免为PetOwner
编写序列化程序/解串器,但需要手动处理pets
以外的字段。
答案 0 :(得分:0)
如果我正确阅读您的问题,您的序列化PetOwner记录不需要包含完整的动物记录 - 他们只需要为每个动物记录包含一个字符串。而且你的并发症来自于你正在存储动物记录这一事实。
假设这是一个准确的陈述,一种方法是注释你的宠物&#39;使用@JsonIgnore字段(或getter,不确定完整的PetOwner类是什么样的),然后添加一个getter,如:
public List<String> getAnimalNames() {
// return a list containing the name of each Animal in this.pets
}
然后,您可以:
List<String>
而不是List<Animal>
,并使用@JsonCreator注释该构造函数,以便Jackson知道使用它(并从提供的名称构造Animal实例)在构造函数内部) public void setAnimalNames(List<String> names) {
// populate this.pets by looking up each pet by name in your registry
}
我必须看到更多你的PetOwner课程和/或更多地了解你打算如何使用它来提供更详细的回复,但我认为这种一般方法(注释PetOwner课程以便杰克逊将忽略&# 39;宠物只能在“动物名称”中进行交易,而应该为您提供您正在寻找的内容。