如何反序列化类型键不固定的多态JSON至Animal子类对象?
解决Jackson动态键多态反序列化问题
你的问题核心是JSON中类型标识不是固定字段,而是以「动物名:类型」的动态键值对存在,默认的Jackson多态注解依赖固定的property字段,所以无法直接生效。以下是两种可行的解决方式:
方法一:自定义反序列化器
通过实现JsonDeserializer,手动解析JSON结构,提取类型、名字和属性,再实例化对应的子类:
1. 编写自定义反序列化器
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; public class AnimalDeserializer extends JsonDeserializer<Animal> { @Override public Animal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); Animal animal = null; // 遍历键值对,找到类型标识项(键为名字,值为类型) for (String key : node.fieldNames()) { String type = node.get(key).asText(); if ("dog".equals(type)) { Dog dog = new Dog(); dog.name = key; if (node.has("barkVolume")) { dog.barkVolume = node.get("barkVolume").asDouble(); } animal = dog; break; } else if ("cat".equals(type)) { Cat cat = new Cat(); cat.name = key; if (node.has("likesCream")) { cat.likesCream = node.get("likesCream").asBoolean(); } if (node.has("lives")) { cat.lives = node.get("lives").asInt(); } animal = cat; break; } } return animal; } }
2. 在Animal类上指定反序列化器
替换原来的多态注解,改用自定义反序列化器:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @JsonDeserialize(using = AnimalDeserializer.class) public static class Animal { public String name; } // Dog和Cat类保持原有定义 @JsonTypeName("dog") public static class Dog extends Animal { public double barkVolume; } @JsonTypeName("cat") public static class Cat extends Animal { boolean likesCream; public int lives; }
3. 测试反序列化
import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; public class Test { public static void main(String[] args) throws IOException { String json = "{\"bulli\":\"dog\",\"barkVolume\":10}"; ObjectMapper mapper = new ObjectMapper(); Animal animal = mapper.readValue(json, Animal.class); if (animal instanceof Dog) { Dog dog = (Dog) animal; System.out.println("Dog name: " + dog.name + ", Bark volume: " + dog.barkVolume); } else if (animal instanceof Cat) { Cat cat = (Cat) animal; System.out.println("Cat name: " + cat.name + ", Likes cream: " + cat.likesCream + ", Lives: " + cat.lives); } } }
方法二:先重构JSON结构
先将原始JSON解析为Map,重构为Jackson多态注解要求的标准格式(添加固定type字段,将动态键转为name字段),再进行反序列化:
1. 重构Map的工具方法
import java.util.HashMap; import java.util.Map; public static Map<String, Object> restructureAnimalJson(Map<String, Object> originalMap) { Map<String, Object> restructuredMap = new HashMap<>(); String name = null; String type = null; // 提取名字和类型 for (Map.Entry<String, Object> entry : originalMap.entrySet()) { if ("dog".equals(entry.getValue()) || "cat".equals(entry.getValue())) { name = entry.getKey(); type = entry.getValue().toString(); break; } } restructuredMap.put("name", name); restructuredMap.put("type", type); // 复制剩余属性 originalMap.remove(name); restructuredMap.putAll(originalMap); return restructuredMap; }
2. 执行反序列化
此方法需要保留你原来的@JsonTypeInfo和@JsonSubTypes注解:
import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.util.Map; public class Test { public static void main(String[] args) throws IOException { String json = "{\"kitty\":\"cat\", \"likesCream\":true, \"lives\":3}"; ObjectMapper mapper = new ObjectMapper(); // 先解析为原始Map Map<String, Object> originalMap = mapper.readValue(json, Map.class); // 重构为标准格式Map Map<String, Object> restructuredMap = restructureAnimalJson(originalMap); // 反序列化为Animal子类 Animal animal = mapper.convertValue(restructuredMap, Animal.class); // 输出测试 if (animal instanceof Cat) { Cat cat = (Cat) animal; System.out.println("Cat name: " + cat.name + ", Likes cream: " + cat.likesCream + ", Lives: " + cat.lives); } } }
内容的提问来源于stack exchange,提问作者griffon vulture
相关产品推荐
相关产品推荐

