
本文详解如何在JAXB中同时解析XML元素的文本值(如220中的220)及其属性、子元素,核心在于启用混合内容(mixed content)支持,并合理使用@XmlMixed与@XmlElementRef。
本文详解如何在jaxb中同时解析xml元素的文本值(如`
在使用JAXB进行XML到Java对象的反序列化时,若XML元素既包含文本内容(PCDATA),又含有子元素和属性(例如
根本原因在于:JAXB默认将元素建模为“纯元素内容”(element-only)或“纯文本内容”(simpleType),而混合内容(mixed content)需显式声明。当XML Schema中对应元素定义为 mixed="true",或在Java类中通过注解主动启用混合模式时,JAXB才能将文本节点与子元素一同捕获并有序组织。
✅ 正确做法:使用 @XmlMixed + @XmlElementRef 组合
对于Java-First开发(无XSD场景),关键是在承载混合内容的字段上添加 @XmlMixed,并配合 @XmlElementRef 声明可能的子元素类型。文本值将作为 String 或 Serializable 实例,与子元素对象一同存入 List
以下为推荐的Element类实现(已适配Jakarta EE 9+,使用jakarta.xml.bind.*):
package org.example.element;
import java.io.Serializable;
import java.util.*;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.annotation.*;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = { "content" })
@XmlRootElement(name = "element")
public class Element implements Serializable {
private static final long serialVersionUID = 20230301L;
@XmlElementRef(name = "subelement", type = JAXBElement.class, required = false)
@XmlMixed
protected List<serializable> content;
public List<serializable> getContent() {
if (content == null) {
content = new ArrayList();
}
return this.content;
}
@XmlAttribute(name = "Id")
protected String id;
@XmlAttribute(name = "Composite")
protected String composite;
// Getters & Setters (omitted for brevity; Lombok @Data is NOT recommended here due to mixed content complexity)
public String getId() { return id; }
public void setId(String value) { this.id = value; }
public String getComposite() { return composite; }
public void setComposite(String value) { this.composite = value; }
}</serializable></serializable>
对应的子元素类 Subelement 示例:
package org.example.element;
import java.io.Serializable;
import jakarta.xml.bind.annotation.*;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "subelement")
public class Subelement implements Serializable {
private static final long serialVersionUID = 20230301L;
@XmlAttribute(name = "Id")
protected String id;
@XmlAttribute(name = "Composite")
protected String composite;
public String getId() { return id; }
public void setId(String value) { this.id = value; }
public String getComposite() { return composite; }
public void setComposite(String value) { this.composite = value; }
}
? 解析结果示例
对如下XML:
<element id="BGM01" composite="true">220<subelement id="S1"></subelement><subelement id="S2"></subelement>456</element>
反序列化后,element.getContent() 将返回长度为5的列表:
[ "220", // String — 文本节点 JAXBElement<subelement>, // subelement with Id="S1" JAXBElement<subelement>, // subelement with Id="S2" "456" // String — 后续文本节点 ]</subelement></subelement>
⚠️ 重要注意事项
- 不要用@XmlValue:@XmlValue仅适用于“无子元素”的简单类型元素(如
John ),一旦存在子元素,JAXB会抛出 IllegalAnnotationException。- 慎用空白字符:XML中用于格式化的换行/缩进会被视为文本节点并加入content列表。如需过滤,应在业务逻辑中处理(例如 content.stream().filter(o -> o instanceof String).map(Object::toString).filter(s -> !s.trim().isEmpty()))。
- 避免Lombok @Data:@XmlMixed字段需手动实现getter/setter以确保初始化(如上例中的getContent()),Lombok自动生成可能引发NullPointerException。
- Schema优先更健壮:若项目允许,建议采用XSD驱动方式,定义 mixed="true" 的complexType,再通过hisrc-higherjaxb-maven-plugin等插件生成带content字段的代码,语义更清晰、类型更安全。
✅ 验证与测试建议
编写JUnit测试验证反序列化行为:
@Test
public void testMixedContentUnmarshal() throws Exception {
JAXBContext ctx = JAXBContext.newInstance(Element.class);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
String xml = "<element id='\"BGM01\"'>220</element>";
Element element = (Element) unmarshaller.unmarshal(new StringReader(xml));
assertEquals("BGM01", element.getId());
assertEquals(1, element.getContent().size());
assertTrue(element.getContent().get(0) instanceof String);
assertEquals("220", element.getContent().get(0));
}
通过以上配置,JAXB即可完整、准确地还原XML中“属性 + 文本 + 子元素”的混合结构,彻底解决文本值丢失问题。










