You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何反序列化类型键不固定的多态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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 21:55:14