JAXB配合Jackson XmlMapper反序列化无界嵌套POJO列表报错
问题根因
该异常由配置缺失叠加逻辑错位导致:
- Jackson XML默认不会自动将单个同名字节点识别为列表类型的单元素集合,遇到映射到List类型的字段只匹配到1个节点时,会尝试把节点内的第一个子属性值直接反序列化为列表元素类型,才会出现尝试用documentId的字符串值
123456构造DocumentDetails对象的报错 - 未给XmlMapper注册JAXB注解支持模块时,POJO上标注的JAXB注解不会完全生效,会进一步加剧节点映射逻辑错位。另外代码中使用
LocalDateTime类型未注册对应时间模块,也会存在后续解析报错的隐患。
解决方案
1. 初始化XmlMapper时开启核心配置、注册必要模块
需要开启单值自动转数组的配置,同时注册JAXB注解支持模块、JSR310时间类型支持模块,初始化代码如下:
XmlMapper xmlMapper = XmlMapper.builder() // 注册JAXB注解支持模块,保证POJO上的JAXB注解正常生效 .addModule(new JaxbAnnotationModule()) // 核心配置:单个匹配节点自动包装为单元素列表 .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) // 注册Java8时间类型支持模块,解决LocalDateTime解析问题 .addModule(new JavaTimeModule()) .build();
注意:上述代码用到的
JaxbAnnotationModule、JavaTimeModule需要引入对应依赖,分别对应jackson-module-jaxb-annotations、jackson-datatype-jsr310包,版本和项目中使用的Jackson核心包版本保持一致即可。
2. 补全POJO类的必要注解
内部类、子实体类需要补全JAXB访问注解、无参构造,时间字段指定匹配的格式,修正后的代码如下:
@Data @NoArgsConstructor @XmlRootElement(name = "getDocumentInfoByClaimNumberResponse") @XmlAccessorType(XmlAccessType.FIELD) public class GetDocumentInfoByClaimNumberRes { @XmlElementWrapper(name = "out") @XmlElement(name = "documentsInfoByClaimNumber") private List<Wrapper> out; @Data @NoArgsConstructor @XmlAccessorType(XmlAccessType.FIELD) public static class Wrapper{ private int caseId; // 无额外包裹层,直接指定平级节点名即可 @XmlElement(name = "documentDetails") private List<DocumentDetails> documentDetails; } }
@Data @NoArgsConstructor @XmlAccessorType(XmlAccessType.FIELD) public class DocumentDetails { private String documentId; private String documentSource; private String category; private String documentType; private String documentName; // 指定和XML匹配的ISO时间格式 @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime createdDate; }
效果说明
- 当XML中仅存在1个
<documentDetails>节点时,配置会自动将其包装为长度为1的List<DocumentDetails>,不会再出现字符串构造实体类的报错 - 当XML中存在2个及以上平级的
<documentDetails>/<documentsInfoByClaimNumber>节点时,Jackson会正常将所有节点收集到对应List中,完全适配无界列表节点的解析要求
内容的提问来源于stack exchange,提问作者DucanoidYoung
相关产品推荐
相关产品推荐

