如何利用Jackson注解实现JSON与多Java类的自定义序列化反序列化?
问题背景
原始输入JSON结构:
{"contactPointId": "123","testString": "41277082","address": {"name1": "name1","name2": "name2","street": "street","houseNumber": "123","zipCode": "45567","country": "276","contactPerson": "Bernd","phone1": "1239","phone2": "456"}}
期望序列化后的JSON结构:
{"contactPointId": "123","testString": "41277082","contact": {"name": "Bernd","phone1": "1239","phone2": "456"},"address": {"name1": "name1","name2": "name2","street": "street","houseNumber": "123","zipCode": "45567","country": "276"}}
现有Java类定义如下:
ContactPoint类
public class ContactPoint { private String contactPointId; private String testString; private Address address; private Contact contact; public ContactPoint( String contactPointId, String testString, Address address, Contact contact ) { this.contactPointId = contactPointId; this.testString = testString; this.address = address; this.contact = contact; } public ContactPoint() { } // getter和setter方法 public String getContactPointId() { return contactPointId; } public void setContactPointId(String contactPointId) { this.contactPointId = contactPointId; } public String getTestString() { return testString; } public void setTestString(String testString) { this.testString = testString; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public Contact getContact() { return contact; } public void setContact(Contact contact) { this.contact = contact; } }
Contact类
public class Contact { private String contactPerson; private String phone1; private String phone2; // getter和setter方法 public String getContactPerson() { return contactPerson; } public void setContactPerson(String contactPerson) { this.contactPerson = contactPerson; } public String getPhone1() { return phone1; } public void setPhone1(String phone1) { this.phone1 = phone1; } public String getPhone2() { return phone2; } public void setPhone2(String phone2) { this.phone2 = phone2; } }
Address类
public class Address { private String name1; private String name2; private String street; private String houseNumber; private String zipCode; private String country; // getter和setter方法 public String getName1() { return name1; } public void setName1(String name1) { this.name1 = name1; } public String getName2() { return name2; } public void setName2(String name2) { this.name2 = name2; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getHouseNumber() { return houseNumber; } public void setHouseNumber(String houseNumber) { this.houseNumber = houseNumber; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } }
需求:通过注解告知Jackson的ObjectMapper从原始JSON的address节点中提取contactPerson、phone1、phone2字段初始化Contact对象,同时序列化时输出目标结构,且不修改现有类的核心结构。
解决方案
方法一:构造器结合@JsonProperty提取嵌套字段
Jackson支持在构造器参数上用@JsonProperty指定嵌套JSON路径,反序列化时直接从address节点提取字段构造Contact对象,序列化时保持现有结构输出:
修改ContactPoint的构造器:
public class ContactPoint { private String contactPointId; private String testString; private Address address; private Contact contact; @JsonCreator public ContactPoint( @JsonProperty("contactPointId") String contactPointId, @JsonProperty("testString") String testString, @JsonProperty("address") Address address, @JsonProperty("address.contactPerson") String contactPerson, @JsonProperty("address.phone1") String phone1, @JsonProperty("address.phone2") String phone2 ) { this.contactPointId = contactPointId; this.testString = testString; this.address = address; // 手动构造Contact对象 this.contact = new Contact(); this.contact.setContactPerson(contactPerson); this.contact.setPhone1(phone1); this.contact.setPhone2(phone2); } // 保留原无参构造器和getter/setter public ContactPoint() {} // ... 原有的getter和setter方法不变 }
此方法无需修改Address和Contact类,仅通过构造器注解完成字段提取,序列化自动输出目标格式。
方法二:自定义@JsonSetter处理字段映射
若不想修改构造器,可在ContactPoint的address字段的setter中,手动从Address提取字段构造Contact:
public class ContactPoint { private String contactPointId; private String testString; private Address address; private Contact contact; // ... 原有的构造器和getter方法不变 // 自定义setter,反序列化时从address中提取contact字段 @JsonSetter public void setAddress(Address address) { this.address = address; // 同时构造Contact对象 if (address != null) { this.contact = new Contact(); this.contact.setContactPerson(address.getContactPerson()); this.contact.setPhone1(address.getPhone1()); this.contact.setPhone2(address.getPhone2()); } } // 序列化时隐藏address中的contact相关字段 @JsonIgnoreProperties({"contactPerson", "phone1", "phone2"}) public Address getAddress() { return address; } // ... 其他getter/setter不变 }
注意:需给Address临时添加contactPerson、phone1、phone2的字段及getter/setter,仅用于反序列化接收字段,序列化时会被忽略。
方法三:自定义反序列化器
若上述方法不满足需求,可编写自定义反序列化器手动处理解析逻辑:
public class ContactPointDeserializer extends StdDeserializer<ContactPoint> { public ContactPointDeserializer() { super(ContactPoint.class); } @Override public ContactPoint deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); ContactPoint contactPoint = new ContactPoint(); contactPoint.setContactPointId(node.get("contactPointId").asText()); contactPoint.setTestString(node.get("testString").asText()); // 解析Address JsonNode addressNode = node.get("address"); Address address = new Address(); address.setName1(addressNode.get("name1").asText()); address.setName2(addressNode.get("name2").asText()); address.setStreet(addressNode.get("street").asText()); address.setHouseNumber(addressNode.get("houseNumber").asText()); address.setZipCode(addressNode.get("zipCode").asText()); address.setCountry(addressNode.get("country").asText()); contactPoint.setAddress(address); // 从addressNode中解析Contact Contact contact = new Contact(); contact.setContactPerson(addressNode.get("contactPerson").asText()); contact.setPhone1(addressNode.get("phone1").asText()); contact.setPhone2(addressNode.get("phone2").asText()); contactPoint.setContact(contact); return contactPoint; } }
在ContactPoint类上指定反序列化器:
@JsonDeserialize(using = ContactPointDeserializer.class) public class ContactPoint { // ... 原有类内容不变 }
此方法完全不修改现有类结构,所有解析逻辑集中在反序列化器中,灵活性最高。
内容的提问来源于stack exchange,提问作者Beanssss
相关产品推荐
相关产品推荐

