Spring Boot中Jackson序列化XML同名子元素列表引发异常
问题场景
在Spring Boot 3.3.5 + jackson-dataformat-xml v2.18.2环境下,PlayerActivityDto中的startingBalanceList和endingBalanceList两个列表,需要将子元素统一序列化为<summaryBalance>标签,但Jackson抛出InvalidDefinitionException,提示getter方法冲突。
异常信息:
Failed to evaluate Jackson serialization for type [class PlayerActivityDto]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Conflicting getter definitions for property "summaryBalance": PlayerActivityDto#getStartingBalanceList() vs PlayerActivityDto#getEndingBalanceList()
问题原因
两个列表字段均使用@JacksonXmlProperty(localName = "summaryBalance"),Jackson会将该注解的localName作为内部属性键,导致两个字段被识别为同一个属性,对应的getter方法因此产生冲突。
解决方案
提供三种可行方案,按需选择:
方案1:为列表字段指定唯一JSON属性名
通过@JsonProperty为每个列表指定唯一的内部属性名,同时保留XML子元素的名称配置:
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import java.util.List; import lombok.Builder; import lombok.Getter; import lombok.Setter; @Builder public class PlayerActivityDto { @JacksonXmlProperty(localName = "playerActivityId", isAttribute = true) private String brandId; @JacksonXmlProperty(localName = "player") private PlayerDto player; @JacksonXmlProperty(localName = "playerActivityDetail") private PlayerActivityDetailDto playerActivityDetail; @Setter @Getter @JsonProperty("startingBalanceList") @JacksonXmlElementWrapper(localName = "startingBalanceList") @JacksonXmlProperty(localName = "summaryBalance") private List<SummaryBalanceDto> startingBalanceList; @Getter @Setter @JsonProperty("endingBalanceList") @JacksonXmlElementWrapper(localName = "endingBalanceList") @JacksonXmlProperty(localName = "summaryBalance") private List<SummaryBalanceDto> endingBalanceList; @JacksonXmlProperty(localName = "playerMovement") @JacksonXmlElementWrapper(localName = "playerMovementList") private List<FundsMovementDto> playerMovementList; }
方案2:在子元素DTO上配置XML根元素名称
在SummaryBalanceDto类上添加@JacksonXmlRootElement指定子元素标签名,移除列表字段上的@JacksonXmlProperty:
// SummaryBalanceDto.java import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; @JacksonXmlRootElement(localName = "summaryBalance") public class SummaryBalanceDto { // 类字段及注解 } // PlayerActivityDto.java中修改列表字段 @Setter @Getter @JacksonXmlElementWrapper(localName = "startingBalanceList") private List<SummaryBalanceDto> startingBalanceList; @Getter @Setter @JacksonXmlElementWrapper(localName = "endingBalanceList") private List<SummaryBalanceDto> endingBalanceList;
方案3:为Getter方法指定唯一属性名
利用Lombok的onMethod_参数,为自动生成的getter添加@JsonGetter,明确属性名:
import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import java.util.List; import lombok.Builder; import lombok.Getter; import lombok.Setter; @Builder public class PlayerActivityDto { @JacksonXmlProperty(localName = "playerActivityId", isAttribute = true) private String brandId; @JacksonXmlProperty(localName = "player") private PlayerDto player; @JacksonXmlProperty(localName = "playerActivityDetail") private PlayerActivityDetailDto playerActivityDetail; @Getter(onMethod_ = {@JsonGetter("startingBalanceList")}) @Setter @JacksonXmlElementWrapper(localName = "startingBalanceList") @JacksonXmlProperty(localName = "summaryBalance") private List<SummaryBalanceDto> startingBalanceList; @Getter(onMethod_ = {@JsonGetter("endingBalanceList")}) @Setter @JacksonXmlElementWrapper(localName = "endingBalanceList") @JacksonXmlProperty(localName = "summaryBalance") private List<SummaryBalanceDto> endingBalanceList; @JacksonXmlProperty(localName = "playerMovement") @JacksonXmlElementWrapper(localName = "playerMovementList") private List<FundsMovementDto> playerMovementList; }
效果验证
上述方案均可实现:
- 列表外层标签分别为
<startingBalanceList>和<endingBalanceList> - 列表内的子元素统一为
<summaryBalance> - 解决Jackson的getter冲突异常
内容的提问来源于stack exchange,提问作者ludvikjr

