Jackson解析含同名属性与子标签的XML时遇类型转换异常
问题场景
需要解析包含child节点的XML:该节点同时存在名为subject的String类型属性,以及未包装的Subject类型集合。通过setSubject(Object value)方法根据传入值的类型,将其映射到对应的String或Subject字段。但第二次调用XmlMapper.convertValue(Object from, Class<T> to)时抛出类型转换异常,错误信息显示node元素被识别为LinkedHashMap而非预期的ArrayList。
错误信息
com.fasterxml.jackson.databind.JsonMappingException: Cannot deserialize value of type
java.util.ArrayList[com.example.MyTest$Node]from Object value (tokenJsonToken.START_OBJECT)
at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.example.MyTest$Root["child"]->com.example.MyTest$Child["subject"])
测试代码与实体类
@Test void syntheticTest() throws JsonMappingException, JsonProcessingException { String xml; Root root; // #1 多个node元素,解析成功 xml = """ <root> <child subject="first subject"> <subject> <node param="value1" /> <node param="value2" /> </subject> </child> </root> """; root = mapper.readValue(xml, Root.class); assertEquals(2, root.getChild().getSubjectObj().getNodes().size()); // #2 单个node元素,抛出异常 xml = """ <root> <child subject="second subject"> <subject> <node param="value1" /> </subject> </child> </root> """; root = mapper.readValue(xml, Root.class); assertEquals(1, root.getChild().getSubjectObj().getNodes().size()); } @Getter @Setter public static class Root { private Child child; } @Getter public static class Child { private String subjectStr; private Subject subjectObj; public void setSubject(Object value) { if (value instanceof String str) { subjectStr = str; } else { subjectObj = mapper.convertValue(value, Subject.class); // <- 此处抛出异常 } } } @Getter @Setter public static class Subject { @JsonProperty("node") @JacksonXmlElementWrapper(useWrapping = false) private List<Node> nodes; } @Getter @Setter public static class Node { private String param; }
核心原因
Jackson XML解析器的默认行为导致了这个问题:
- 当XML节点下存在多个同名子元素时,解析器会自动将它们封装为
ArrayList集合; - 当XML节点下只有一个同名子元素时,解析器会直接将其解析为单个对象(这里对应
LinkedHashMap,因为Jackson先把XML节点转成通用的Map结构)。
而Subject类中nodes字段声明为List<Node>,调用convertValue时,Jackson尝试将单个LinkedHashMap对象转换成ArrayList<Node>,类型不匹配直接触发JsonMappingException。第一次调用成功是因为多个node元素被解析为ArrayList,与目标类型匹配。
内容的提问来源于stack exchange,提问作者meridbt

