轉換器我有一組簡單的,我想序列化/使用傑克遜(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
名單的名字。反序列化時,我想將這些名稱映射回使用預先存在的AnimalRegistry
的Animal
對象。
隨着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
以外的字段。
'PetOwner'只是一個數據類。它沒有(也不應該獲得)對上下文(「AnimalRegistry」)的訪問權限,允許它將動物名稱映射到動物。所以不幸的是,你的建議都不適合我。 – user686249 2015-02-05 21:20:45
這很有道理,我甚至都沒有想過。我經常使用的一種方法是定義一個嚴格用於持久性/傳輸的數據類(例如,它被註釋了,具有setter的可變字段等),它公開了一個將其轉換爲「真實」版本的方法同一類 - 我想轉換方法可以通過資源,如AnimalRegistry參數化。然而,關於這種方法的典型抱怨是它的代碼太多了......所以,考慮到你希望減少代碼,這種方法可能對你也不適用。 – wdf 2015-02-05 21:34:56
事實上,我期待的解決方案並不比JAXB的'XmlAdapter'方法重要得多。 – user686249 2015-02-06 10:03:58