java - 在不使用 @JsonTypeInfo 的情况下使用 Jackson 反序列化为多态集合

标签 java json jackson

我正在使用第 3 方服务,它输出 JSON,我试图将其反序列化为 Java POJO。无法更改 JSON 服务,我使用的是 Java 8 和 Jackson 2.4。

这是我的问题的简化版本。我在下面有一个接口(interface)和两个具体类:

public interface Animal {

}

public class Dog implements Animal {

   public String bark;

}

public class Cat implements Animal {

   public String meow;

}

我需要将以下 JSON 反序列化为 List<Animal> :

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

我希望能够根据 JSON 中是否存在 meow 属性(它是 Cat)和 bark 属性(它是 Dog)来确定具体的 java 类型。如何才能让我 jackson 做到这一点?

最佳答案

它可以通过自定义实现 StdDeserializer 来实现:

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);
    }
}

可以按如下方式使用:

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);

更多详情请看here .

关于java - 在不使用 @JsonTypeInfo 的情况下使用 Jackson 反序列化为多态集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38490969/

相关文章:

java - 获取Java泛型的类,以及泛型的接口(interface)实现

java - 无效 key 异常 java.security.InvalidKeyException : No installed provider supports this key: (null)

java - 如何获取四方 field 列表 - Android

Python 3 : JSON File Load with Non-ASCII Characters

java - 序列化和反序列化 Map<Object, Object> Jackson

Java - 使用 getters/setters 而不是反射进行反序列化

java - 如何动态地将 JSON 转换为数据库表?

java - 更新现有的 Yaml 文件

json - 更新到 Xcode 8.1 : Can no longer convert JSON data of type AnyObject to expected Type

Java Jackson JSON 解析为 Map<String, String>