2016-07-20 6 views
2

Ich verwende einen 3rd-Party-Service, der JSON ausspuckt, die ich in Java-POJOs deserialisieren möchte. Der JSON-Dienst kann nicht geändert werden und ich verwende Java 8 und Jackson 2.4.Deserialisieren in polymorphe Sammlung mit Jackson ohne Verwendung von @JsonTypeInfo

Hier ist eine vereinfachte Version meines Problems. Ich habe eine Schnittstelle und zwei konkrete Klassen unter:

public interface Animal { 

} 

public class Dog implements Animal { 

    public String bark; 

} 

public class Cat implements Animal { 

    public String meow; 

} 

ich folgende JSON in ein List<Animal> deserialisieren brauche:

{ 
     "animals": [ 
    { 
     "bark":"bowwow" 
    }, 
    { 
     "bark":"woofWoof" 
    }, 
    { 
     "meow":"meeeOwww" 
    }, 
    { 
     "meow":"hisssss" 
    } 
    ] 
} 

Ich mag den konkreten Java-Typen bestimmen können, auf der Grundlage der Existenz der Miau-Eigenschaft (es ist eine Katze) und der Rinde-Eigenschaft (es ist ein Hund) in der JSON. Wie kann ich das tun, Jackson?

Antwort

1

Es könnte mit einer benutzerdefinierten Implementierung von StdDeserializer erreicht werden:

public class UniquePropertyPolymorphicDeserializer<T> extends StdDeserializer<T> { 

    private Map<String, Class<? extends T>> registry; 

    public UniquePropertyPolymorphicDeserializer(Class<T> clazz) { 
     super(clazz); 
     registry = new HashMap<String, Class<? extends T>>(); 
    } 

    public void register(String uniqueProperty, Class<? extends T> clazz) { 
     registry.put(uniqueProperty, clazz); 
    } 

    @Override 
    public T deserialize(JsonParser p, DeserializationContext ctxt) 
      throws IOException, JsonProcessingException { 

     Class<? extends T> clazz = null; 

     ObjectMapper mapper = (ObjectMapper) p.getCodec(); 
     ObjectNode obj = (ObjectNode) mapper.readTree(p); 
     Iterator<Entry<String, JsonNode>> elementsIterator = obj.fields(); 

     while (elementsIterator.hasNext()) { 
      Entry<String, JsonNode> element = elementsIterator.next(); 
      String name = element.getKey(); 
      if (registry.containsKey(name)) { 
       clazz = registry.get(name); 
       break; 
      } 
     } 

     if (clazz == null) { 
      throw ctxt.mappingException(
        "No registered unique properties found " 
        + "for polymorphic deserialization"); 
     } 

     return mapper.treeToValue(obj, clazz); 
    } 
} 

Es wie folgt verwendet werden:

String json = "[{\"bark\":\"bowwow\"},{\"bark\":\"woofWoof\"},{\"meow\":\"meeeOwww\"},{\"meow\":\"hisssss\"}]"; 

UniquePropertyPolymorphicDeserializer<Animal> deserializer = 
    new UniquePropertyPolymorphicDeserializer<>(Animal.class); 

deserializer.register("bark", Dog.class); // if "bark" field is present, then it's a Dog 
deserializer.register("meow", Cat.class); // if "meow" field is present, then it's a Cat 

SimpleModule module = new SimpleModule("UniquePropertyPolymorphicDeserializer", 
     new Version(1, 0, 0, null, "com.example", "polymorphic-deserializer")); 
module.addDeserializer(Animal.class, deserializer); 

ObjectMapper mapper = new ObjectMapper(); 
mapper.registerModule(module); 

Animal[] animals = mapper.readValue(json, Animal[].class); 

Weitere Informationen here einen Blick.

Verwandte Themen