如何基于属性名使用Jackson反序列化同基类的不同JSON对象?
这个问题确实有点特别——Jackson默认的多态反序列化都是靠值里的类型标识来区分子类,但你这儿是靠顶级键名来判断类型,不过咱们可以通过自定义反序列化器完美解决这个需求,下面一步步来实现:
解决方案:自定义Jackson反序列化器实现基于顶级键的多态反序列化
第一步:定义基类和子类
先把我们的基类和对应子类写好,对应JSON里的foo和bar键:
// 基类 public abstract class BaseClass { protected String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } } // 对应{"foo": "..."}的子类 public class FooClass extends BaseClass { // 这里可以添加Foo专属的属性或方法 } // 对应{"bar": "..."}的子类 public class BarClass extends BaseClass { // 这里可以添加Bar专属的属性或方法 }
第二步:编写自定义反序列化器
核心逻辑就在这儿——我们要在反序列化时读取JSON的顶级键,判断是foo还是bar,然后实例化对应的子类:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; public class BaseClassDeserializer extends StdDeserializer<BaseClass> { protected BaseClassDeserializer() { super(BaseClass.class); } @Override public BaseClass deserialize(JsonParser parser, DeserializationContext context) throws IOException { JsonNode rootNode = parser.getCodec().readTree(parser); // 检查顶级键,匹配对应的子类 if (rootNode.has("foo")) { FooClass foo = new FooClass(); foo.setValue(rootNode.get("foo").asText()); return foo; } else if (rootNode.has("bar")) { BarClass bar = new BarClass(); bar.setValue(rootNode.get("bar").asText()); return bar; } // 没有匹配键时的处理,根据业务需求调整 throw new IOException("无法识别的类型:顶级键不是foo或bar"); } }
第三步:注册反序列化器(两种方式选一种)
方式一:用注解绑定到基类
直接在基类上添加注解,让Jackson自动使用我们的反序列化器:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @JsonDeserialize(using = BaseClassDeserializer.class) public abstract class BaseClass { // 基类内容不变 }
方式二:手动注册到ObjectMapper
如果不想修改基类的注解,可以手动配置ObjectMapper:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; public class TestDeserialization { public static void main(String[] args) throws IOException { ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(BaseClass.class, new BaseClassDeserializer()); mapper.registerModule(module); // 测试反序列化Foo类型 String fooJson = "{\"foo\": \"String value\"}"; BaseClass fooInstance = mapper.readValue(fooJson, BaseClass.class); System.out.println(fooInstance.getClass().getSimpleName()); // 输出 FooClass System.out.println(fooInstance.getValue()); // 输出 String value // 测试反序列化Bar类型 String barJson = "{\"bar\": \"String value\"}"; BaseClass barInstance = mapper.readValue(barJson, BaseClass.class); System.out.println(barInstance.getClass().getSimpleName()); // 输出 BarClass System.out.println(barInstance.getValue()); // 输出 String value } }
扩展小提示
- 如果后续要加新的子类(比如
BazClass对应baz键),只需要在反序列化器的判断逻辑里加新分支就行,扩展性拉满。 - 如果子类有复杂属性,不用手动赋值,可以用
parser.getCodec().treeToValue()直接把节点转成子类对象,比如:
前提是子类字段和JSON结构匹配哦~if (rootNode.has("foo")) { return parser.getCodec().treeToValue(rootNode.get("foo"), FooClass.class); }
内容的提问来源于stack exchange,提问作者user1925405
相关产品推荐
相关产品推荐

