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

如何用Jackson根据category字段反序列化多类型Value的JSON?

基于Jackson根据父对象字段动态反序列化子对象类型

场景说明

有如下JSON结构,result中的value类型由同层级的category字段决定:

{
    "result": {
        "id": "123", 
        "category": "typeA",
        "value": {
            "typeAField": "123"
        }
    }
}
{
    "result": {
        "id": "321", 
        "category": "typeB",
        "value": {
            "typeBField1": "123",
            "typeBField2": true
        }
    }
}

对应的Java类结构已定义:

public class Result { 
  String id;
  String category;
  Values value;
}

public abstract class Values {
    
}

public class TypeA extends Values {
    String typeAField;
}

public class TypeB extends Values {
    String typeBField1;
    Boolean typeBField2;
}

遇到的问题

尝试使用@JsonTypeInfo注解时抛出错误:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'typeAField' as a subtype of `Values`: known type ids = [typeA, typeB] (for POJO property 'value')
 at [Source: (String)"{"id":"123","category":"typeA","value":{"typeAField":"123"}}"; line: 1, column: 41] (through reference chain: Result["value"])

目前通过自定义@JsonSetter实现了功能,但代码冗余,需要更简洁的方案。


最优解决方案:结合@JsonTypeInfo与@JsonTypeIdResolver

因为类型标识(category)不在value对象内部,而是在其父对象Result中,所以需要自定义类型解析器从父对象获取类型id:

步骤1:自定义TypeIdResolver

import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;

public class ValuesTypeIdResolver extends TypeIdResolverBase {

    @Override
    public Id getMechanism() {
        return Id.NAME;
    }

    @Override
    public String idFromValue(Object value) {
        if (value instanceof TypeA) {
            return "typeA";
        } else if (value instanceof TypeB) {
            return "typeB";
        }
        return null;
    }

    @Override
    public String idFromValueAndType(Object value, Class<?> suggestedType) {
        return idFromValue(value);
    }

    @Override
    public JavaType typeFromId(DatabindContext context, String id) {
        switch (id) {
            case "typeA":
                return context.constructType(TypeA.class);
            case "typeB":
                return context.constructType(TypeB.class);
            default:
                throw new IllegalArgumentException("Unknown category: " + id);
        }
    }
}

步骤2:配置Result类的value字段

在Result类的value字段上添加注解,指定类型解析规则:

import com.fasterxml.jackson.annotation.JsonTypeInfo;

public class Result { 
  String id;
  String category;
  
  @JsonTypeInfo(
      use = JsonTypeInfo.Id.NAME,
      include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
      property = "category",
      visible = true,
      resolver = ValuesTypeIdResolver.class
  )
  Values value;
  
  // 添加getter/setter保证Jackson可访问字段
  public String getId() { return id; }
  public void setId(String id) { this.id = id; }
  public String getCategory() { return category; }
  public void setCategory(String category) { this.category = category; }
  public Values getValue() { return value; }
  public void setValue(Values value) { this.value = value; }
}

步骤3:验证反序列化

import com.fasterxml.jackson.databind.ObjectMapper;

public class Test {
    public static void main(String[] args) throws Exception {
        String json1 = "{\"id\":\"123\",\"category\":\"typeA\",\"value\":{\"typeAField\":\"123\"}}";
        ObjectMapper mapper = new ObjectMapper();
        Result result1 = mapper.readValue(json1, Result.class);
        System.out.println(result1.getValue().getClass()); // 输出class TypeA
        
        String json2 = "{\"id\":\"321\",\"category\":\"typeB\",\"value\":{\"typeBField1\":\"123\",\"typeBField2\":true}}";
        Result result2 = mapper.readValue(json2, Result.class);
        System.out.println(result2.getValue().getClass()); // 输出class TypeB
    }
}

方案优势

  • 完全基于Jackson注解实现,代码简洁,无需手动处理JSON节点
  • 同时支持序列化和反序列化
  • 类型解析逻辑集中在Resolver类,易于维护和扩展

内容的提问来源于stack exchange,提问作者carrot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 07:00:33