2015-02-05 75 views
1

轉換器我有一組簡單的,我想序列化/使用傑克遜(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名單的名字。反序列化時,我想將這些名稱映射回使用預先存在的AnimalRegistryAnimal對象。

隨着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

如果我正確地閱讀你的問題,你的序列化PetOwner記錄不需要包含完整的動物記錄 - 他們只需要爲每個動物記錄包含一個字符串。而你的複雜性來自於你存儲動物記錄的事實。

假設這是一個準確的說法,一個方法是註釋你的「寵物」字段(或者,消氣,不知道你的全PetOwner類的樣子)與@JsonIgnore,然後添加一個getter如:

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

然後,您可以:

  1. 添加PetOwner一個可選的構造,需要一個List<String>而非List<Animal>,並標註該構造與@JsonCreator讓傑克遜知道使用它(和構造您來自th的動物實例E中提供構造函數中的名稱)
  2. 如果您使用的是默認值(0-ARG)構造函數中添加一個setter,例如(和刪除任何現有的setter方法寵物):

public void setAnimalNames(List<String> names) { // populate this.pets by looking up each pet by name in your registry }

我將不得不看到更多的你的PetOwner類和/或知道更多關於你打算如何使用它來給出更詳細的響應,但我認爲這種一般方法(註釋PetOwner類,以便傑克遜只會忽略「寵物」和交易只有在'animalNames'中)應該會給你你想要的東西。

+0

'PetOwner'只是一個數據類。它沒有(也不應該獲得)對上下文(「AnimalRegistry」)的訪問權限,允許它將動物名稱映射到動物。所以不幸的是,你的建議都不適合我。 – user686249 2015-02-05 21:20:45

+0

這很有道理,我甚至都沒有想過。我經常使用的一種方法是定義一個嚴格用於持久性/傳輸的數據類(例如,它被註釋了,具有setter的可變字段等),它公開了一個將其轉換爲「真實」版本的方法同一類 - 我想轉換方法可以通過資源,如AnimalRegistry參數化。然而,關於這種方法的典型抱怨是它的代碼太多了......所以,考慮到你希望減少代碼,這種方法可能對你也不適用。 – wdf 2015-02-05 21:34:56

+0

事實上,我期待的解決方案並不比JAXB的'XmlAdapter'方法重要得多。 – user686249 2015-02-06 10:03:58