杰克逊用上下文反序列化适配器/转换器

时间:2015-02-05 15:24:54

标签: java json serialization jackson yaml

我有一组简单的类,我想使用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以外的字段。

1 个答案:

答案 0 :(得分:0)

如果我正确阅读您的问题,您的序列化PetOwner记录不需要包含完整的动物记录 - 他们只需要为每个动物记录包含一个字符串。而且你的并发症来自于你正在存储动物记录这一事实。

假设这是一个准确的陈述,一种方法是注释你的宠物&#39;使用@JsonIgnore字段(或getter,不确定完整的PetOwner类是什么样的),然后添加一个getter,如:

public List<String> getAnimalNames() {
  // return a list containing the name of each Animal in this.pets 
}

然后,您可以:

  1. 为PetOwner添加一个替代构造函数,它接受List<String>而不是List<Animal>,并使用@JsonCreator注释该构造函数,以便Jackson知道使用它(并从提供的名称构造Animal实例)在构造函数内部)
  2. 如果您正在使用默认(0-arg)构造函数,请添加一个setter,例如(并删除任何现有的宠物setter):
  3. public void setAnimalNames(List<String> names) { // populate this.pets by looking up each pet by name in your registry }

    我必须看到更多你的PetOwner课程和/或更多地了解你打算如何使用它来提供更详细的回复,但我认为这种一般方法(注释PetOwner课程以便杰克逊将忽略&# 39;宠物只能在“动物名称”中进行交易,而应该为您提供您正在寻找的内容。