Jackson注解实现多格式JSON反序列化:处理可选嵌套包装
问题描述
需要处理来自不同数据源的JSON数据,核心地址内容一致,但嵌套结构不同:
第一种JSON结构:
{ "addressData": { "street": "Foostreet 1", "zip": "12345", "city": "Metropolis" } }
第二种JSON结构:
{ "addressData": { "address": { "street": "Foostreet 1", "zip": "12345", "city": "Metropolis" } } }
当前使用的Java反序列化模型需要冗余的Address类和额外后处理来归一化数据:
class AddressData { private String street; private String zip; private String city; private Address address; } class Address { private String street; private String zip; private String city; }
请问是否存在Jackson注解,可让Jackson识别这种可选的嵌套包装,省去冗余代码和后处理步骤?
解决方案
当然可以,以下几种Jackson注解/方案都能实现这种多结构兼容,无需额外后处理:
方法1:用@JsonAnySetter动态捕获嵌套字段
修改AddressData类,添加@JsonAnySetter方法处理address字段,直接将嵌套属性赋值到当前对象:
class AddressData { private String street; private String zip; private String city; // 省略getter/setter,可通过Lombok简化 @JsonAnySetter public void handleNestedAddress(String key, Object value) { if ("address".equals(key) && value instanceof Map) { Map<String, Object> addressMap = (Map<String, Object>) value; this.street = (String) addressMap.get("street"); this.zip = (String) addressMap.get("zip"); this.city = (String) addressMap.get("city"); } } }
这种方式既能直接映射顶层的street/zip/city,也能处理嵌套在address里的字段,自动完成归一化。
方法2:@JsonAlias结合类型安全的Setter
如果追求类型安全,可以保留Address类,给接收嵌套对象的Setter添加@JsonAlias,同时保留直接映射字段的Setter:
class AddressData { private String street; private String zip; private String city; // 直接映射顶层字段的Setter public void setStreet(String street) { this.street = street; } public void setZip(String zip) { this.zip = zip; } public void setCity(String city) { this.city = city; } // 处理嵌套的address字段,将属性赋值到当前对象 @JsonAlias("address") public void setNestedAddress(Address address) { if (address != null) { this.street = address.getStreet(); this.zip = address.getZip(); this.city = address.getCity(); } } } class Address { private String street; private String zip; private String city; // 省略getter/setter }
Jackson会自动适配两种结构:遇到顶层字段调用对应Setter,遇到address字段则调用setNestedAddress。
方法3:自定义反序列化器(最灵活)
如果需要处理更复杂的兼容逻辑,可以用@JsonDeserialize指定自定义反序列化器:
class AddressDataDeserializer extends StdDeserializer<AddressData> { public AddressDataDeserializer() { super(AddressData.class); } @Override public AddressData deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); AddressData addressData = new AddressData(); // 优先尝试顶层字段 if (node.has("street")) { addressData.setStreet(node.get("street").asText()); addressData.setZip(node.get("zip").asText()); addressData.setCity(node.get("city").asText()); } // 再尝试嵌套的address节点 else if (node.has("address")) { JsonNode addressNode = node.get("address"); addressData.setStreet(addressNode.get("street").asText()); addressData.setZip(addressNode.get("zip").asText()); addressData.setCity(addressNode.get("city").asText()); } return addressData; } } // 在AddressData类上指定反序列化器 @JsonDeserialize(using = AddressDataDeserializer.class) class AddressData { private String street; private String zip; private String city; // 省略getter/setter }
这种方式完全由你控制反序列化逻辑,适合复杂场景。
内容的提问来源于stack exchange,提问作者Yasammez
相关产品推荐
相关产品推荐

