To handle inconsistent or legacy JSON field names—such as database column prefixes (e.g., st_id, st_name)—while mapping to a unified domain model, annotations can declare multiple source keys for a single target property. This avoids manual preprocessing and improves maintainability.
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@Data
public class ProfileRecord {
@JSONField(name = "id", alternateNames = {"st_id", "user_id"})
private String identifier;
@JSONField(name = "fullName", alternateNames = {"st_name", "display_name"})
private String displayName;
@JSONField(name = "yearsOld", alternateNames = {"st_age", "age_years"})
private Integer ageInYears;
@JSONField(name = "gender", alternateNames = {"st_sex", "sex_code"})
private String genderCode;
@JSONField(
name = "dob",
alternateNames = {"st_birthDate", "birth_dt"},
format = "yyyy-MM-dd"
)
private LocalDate dateOfBirth;
}
Generic Deserialization with Type Erasure Compensation
When deserializing nested generic responses (e.g., API wrappers), raw type information is erased at runtime. Using TypeReference preserves the full parameterized type during parsing:
Response<StudentProfile> result =
JSON.parseObject(rawJson, new TypeReference<Response<StudentProfile>>() {});
XML Serialization with Structural Precision
For XML-based integrations—especially with legacy systems—precise control over element nesting, attribute usage, and collection flattening is essential. Jackson’s XML module provides fine-grained annotations to enforce canonical structure.
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.12.4</version>
</dependency>
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.Data;
import java.util.List;
@Data
@JacksonXmlRootElement(localName = "profile")
public class ProfileRecord {
@JacksonXmlProperty(localName = "identifier")
private String identifier;
@JacksonXmlProperty(localName = "displayName")
private String displayName;
@JacksonXmlProperty(localName = "ageInYears")
private Integer ageInYears;
@JacksonXmlProperty(localName = "genderCode")
private String genderCode;
@JacksonXmlProperty(localName = "dateOfBirth")
private String dateOfBirth;
@JacksonXmlProperty(localName = "interest")
@JacksonXmlElementWrapper(useWrapping = false)
private List<InterestItem> interests;
@JacksonXmlProperty(localName = "associate")
private AssociateRef associate;
}
@Data
@JacksonXmlRootElement(localName = "interest")
public class InterestItem {
@JacksonXmlProperty(localName = "activity")
private String activityName;
}
@Data
@JacksonXmlRootElement(localName = "associate")
public class AssociateRef {
@JacksonXmlProperty(localName = "label", isAttribute = true)
private String label;
@JacksonXmlProperty(localName = "yearsKnown", isAttribute = true)
private Integer tenureYears;
}
Without @JacksonXmlElementWrapper(useWrapping = false), a list of <interest> would be wrapped inside an extraneous <interests> cnotainer—violating expected schema contracts.
XML ↔ Object Conversion
Use XmlMapper for bidirectional transformation, ensuring consistent namsepace handling and formatting options:
XmlMapper xmlMapper = new XmlMapper();
// Parse XML string into object graph
ProfileRecord record = xmlMapper.readValue(xmlPayload, ProfileRecord.class);
// Serialize object back to canonical XML
String serializedXml = xmlMapper.writeValueAsString(record);