主XSD与公共XSD命名空间不同致JAXB反序列化失败求助
问题描述
使用maven-jaxb2-plugin从两个不同命名空间的XSD生成Java类:主XSD为message.xsd(命名空间message),导入了通用类型所在的common.xsd(命名空间common)。生成代码后,JAXB反序列化XML时无法读取字段,只能得到默认值;手动修改生成类中InfoType的字段命名空间为message后可正常工作。但无法修改XSD的命名空间定义,也不想每次生成代码后手动修改,需解决以下问题:
- 是否能让两个生成类的字段处于同一命名空间?
- XML消息是否存在错误?
- 是否遗漏了JAXB2生成器参数?
- 是否需要使用JAXB绑定解决?
生成的关键代码
Message类
@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "computer" }) @XmlRootElement(name = "message", namespace = "message") public class Message implements ToString2 { @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "date", "info" }) public static class Computer implements ToString2 { @XmlElement(namespace = "message", required = true) protected int date; @XmlElement(name = "info", namespace = "message", required = true) protected InfoType info; } }
InfoType类(来自common.xsd)
@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "InfoType", propOrder = { "number", "id" }) public class InfoType implements ToString2 { @XmlElement(namespace = "") @XmlSchemaType(name = "integer") protected int number; @XmlElement(namespace = "") @XmlSchemaType(name = "integer") protected int id; }
待反序列化的XML
<?xml version="1.0" encoding="utf-8"?> <message xmlns="message"> <computer> <info> <name>2</name> <id>999998</id> </info> <date>20220508</date> </computer> </message>
package-info.java
@javax.xml.bind.annotation.XmlSchema(namespace = "common", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package com.myapp.message;
Maven插件配置
<plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <version>0.14.0</version> <dependencies> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>2.3.3</version> </dependency> </dependencies> <configuration> <accessExternalSchema>all</accessExternalSchema> <specVersion>2.1</specVersion> <cleanPackageDirectories>false</cleanPackageDirectories> <extension>true</extension> <args> <arg>-Xfluent-api</arg> <arg>-XtoString</arg> </args> <plugins> <plugin> <groupId>net.java.dev.jaxb2-commons</groupId> <artifactId>jaxb-fluent-api</artifactId> <version>${maven-jaxb2-plugin.fluent-api.version}</version> </plugin> <plugin> <groupId>org.jvnet.jaxb2_commons</groupId> <artifactId>jaxb2-basics</artifactId> <version>${jaxb2-basics-runtime.version}</version> </plugin> </plugins> </configuration> <executions> <execution> <id>generate-message</id> <phase>generate-sources</phase> <goals> <goal>generate</goal> </goals> <configuration> <schemaDirectory>src/main/resources/message</schemaDirectory> <schemaIncludes> <include>message.xsd</include> </schemaIncludes> <bindingDirectory>src/main/resources/jaxb</bindingDirectory> <generateDirectory>target/generated-sources/jaxb/message</generateDirectory> <generatePackage>com.myapp.message </generatePackage> </configuration> </execution> </executions> </plugin>
问题原因
- 命名空间不匹配:
InfoType的字段被生成时指定了空命名空间,但XML中<info>的子元素继承了根元素的message命名空间,JAXB无法匹配字段,因此使用默认值。 - XML字段错误:XML中的
<name>与InfoType的number字段不对应,即使命名空间正确也无法解析该字段。 - package-info配置冲突:当前
package-info指定默认命名空间为common,但Message类的命名空间是message,导致生成的类默认命名空间不一致。
解决方案
方案1:修正XML消息格式(若允许修改XML)
按照XSD的命名空间定义,<info>及其子元素属于common命名空间,需显式声明:
<?xml version="1.0" encoding="utf-8"?> <message xmlns="message" xmlns:common="common"> <computer> <info xmlns="common"> <number>2</number> <!-- 修正为InfoType的number字段 --> <id>999998</id> </info> <date>20220508</date> </computer> </message>
方案2:使用JAXB绑定文件统一命名空间(不修改XML)
创建绑定文件src/main/resources/jaxb/bindings.xjb,强制将common.xsd生成的类字段映射到message命名空间:
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" jaxb:version="2.1"> <!-- 将common.xsd的目标命名空间映射为message --> <jaxb:bindings schemaLocation="../message/common.xsd" node="/xsd:schema"> <jaxb:schemaBindings> <jaxb:package name="com.myapp.message"> <jaxb:xmlns prefix="" namespace="message"/> </jaxb:package> </jaxb:schemaBindings> </jaxb:bindings> <!-- 为InfoType的所有字段指定message命名空间 --> <jaxb:bindings schemaLocation="../message/common.xsd" node="/xsd:schema/xsd:complexType[@name='InfoType']/xsd:sequence/xsd:element"> <jaxb:property> <jaxb:XmlElement namespace="message"/> </jaxb:property> </jaxb:bindings> </jaxb:bindings>
在Maven插件配置中添加绑定文件引用:
<execution> <id>generate-message</id> <!-- 其他配置不变 --> <configuration> <!-- 新增绑定文件包含规则 --> <bindingIncludes> <include>bindings.xjb</include> </bindingIncludes> </configuration> </execution>
重新生成代码后,InfoType的字段会自动带有@XmlElement(namespace = "message")注解,与XML中的命名空间匹配。
方案3:调整package-info的默认命名空间
通过绑定文件修改生成的package-info,将默认命名空间设为message,避免命名空间冲突:
在绑定文件中添加:
<jaxb:bindings schemaLocation="../message/message.xsd" node="/xsd:schema"> <jaxb:schemaBindings> <jaxb:package name="com.myapp.message"> <jaxb:xmlns prefix="" namespace="message"/> <jaxb:elementFormDefault>QUALIFIED</jaxb:elementFormDefault> </jaxb:package> </jaxb:schemaBindings> </jaxb:bindings>
生成的package-info会变为:
@javax.xml.bind.annotation.XmlSchema(namespace = "message", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package com.myapp.message;
这样包内所有类的默认命名空间统一为message,无需单独指定字段的命名空间。
内容的提问来源于stack exchange,提问作者PureVodka
相关产品推荐
相关产品推荐

