JAXB 4.X.X序列化异常:子类同名字段致父类字段无法序列化
问题原因与解决方案
问题背景
将JAXB从3.0.2升级到4.0.5后,序列化ChildA对象时,父类Parent的ID字段始终无法被序列化。回溯确认问题首次出现在JAXB 4.0.0版本,且仅当ChildB类存在Id字段时触发;移除ChildB的Id字段或回退到JAXB 3.0.2版本,序列化恢复正常。
原因分析
JAXB 4.x版本对属性冲突检测与继承元数据处理逻辑做了变更:
- JAXB 3.x中,父类
Parent的ID字段与子类ChildB的Id字段被视为两个独立XML元素(XML元素名区分大小写),元数据互不干扰。 - JAXB 4.x中,默认元数据合并逻辑会将大小写不同但名称相似的属性(
ID与Id)判定为冲突属性。当ChildB存在时,JAXB会认为子类的Id覆盖了父类的ID,导致整个Parent类的ID字段在序列化时被标记为"已被覆盖",进而影响所有继承自Parent的子类(包括ChildA)的序列化行为。
无需修改WSDL的解决方案
方案1:通过JAXB绑定文件明确字段映射
创建绑定文件bindings.xjb,强制指定每个字段对应的XML元素名,避免冲突检测误判:
<jaxb:bindings xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="3.0"> <!-- 明确Parent类的ID字段映射 --> <jaxb:bindings schemaLocation="test.wsdl" node="//xsd:complexType[@name='Parent']/xsd:sequence/xsd:element[@name='ID']"> <jaxb:property name="ID"/> </jaxb:bindings> <!-- 明确ChildB类的Id字段映射 --> <jaxb:bindings schemaLocation="test.wsdl" node="//xsd:complexType[@name='ChildB']/xsd:sequence/xsd:element[@name='Id']"> <jaxb:property name="Id"/> </jaxb:bindings> </jaxb:bindings>
在jaxb-maven-plugin中配置绑定文件:
<plugin> <groupId>org.jvnet.jaxb</groupId> <artifactId>jaxb-maven-plugin</artifactId> <version>4.0.6</version> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>${jaxb.version}</version> </dependency> </dependencies> <configuration> <generatePackage>org.example</generatePackage> <schemaDirectory>src/main/resources</schemaDirectory> <schemaIncludes> <include>*.wsdl</include> </schemaIncludes> <!-- 添加绑定文件配置 --> <bindingIncludes> <include>bindings.xjb</include> </bindingIncludes> </configuration> </plugin>
方案2:配置JAXB上下文的命名策略
创建Jaxb2Marshaller时,设置属性强制使用区分大小写的命名策略:
Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); marshaller.setContextPath("org.example"); // 启用区分大小写的命名策略 Map<String, Object> props = new HashMap<>(); props.put("jakarta.xml.bind.naming.strategy", "org.glassfish.jaxb.runtime.v2.runtime.DefaultNamingStrategy"); marshaller.setMarshallerProperties(props);
该策略会严格区分XML元素名的大小写,避免将ID和Id判定为同一属性。
方案3:通过绑定文件添加自定义注解
若上述方案无效,可通过绑定文件为生成的类添加@XmlElement注解,明确指定XML元素名:
<jaxb:bindings xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:annox="http://annox.dev.java.net" version="3.0"> <jaxb:bindings schemaLocation="test.wsdl" node="//xsd:complexType[@name='Parent']"> <annox:annotateClass> <annox:annotate annox:class="jakarta.xml.bind.annotation.XmlAccessorType" value="FIELD"/> </annox:annotateClass> <jaxb:bindings node="//xsd:sequence/xsd:element[@name='ID']"> <annox:annotateField> <annox:annotate annox:class="jakarta.xml.bind.annotation.XmlElement" name="ID"/> </annox:annotateField> </jaxb:bindings> </jaxb:bindings> <jaxb:bindings schemaLocation="test.wsdl" node="//xsd:complexType[@name='ChildB']"> <jaxb:bindings node="//xsd:sequence/xsd:element[@name='Id']"> <annox:annotateField> <annox:annotate annox:class="jakarta.xml.bind.annotation.XmlElement" name="Id"/> </annox:annotateField> </jaxb:bindings> </jaxb:bindings> </jaxb:bindings>
需在jaxb-maven-plugin中添加annox依赖:
<plugin> <!-- 其他配置保持不变 --> <dependencies> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>${jaxb.version}</version> </dependency> <dependency> <groupId>org.jvnet.jaxb2_commons</groupId> <artifactId>jaxb2-basics-annotate</artifactId> <version>1.1.1</version> </dependency> </dependencies> </plugin>
内容的提问来源于stack exchange,提问作者halp_me_plz
相关产品推荐
相关产品推荐

