如何在JAXB RI中使用自定义命名空间?解决属性不支持报错
问题解决:JAXB RI自定义命名空间前缀映射配置
错误原因
- 属性名错误:JAXB RI(Jakarta EE 4+)中,正确的命名空间前缀映射属性为
jakarta.xml.bind.marshaller.NAMESPACE_PREFIX_MAPPER,而非你使用的org.glassfish.jaxb.namespacePrefixMapper或内部runtime属性。 - 依赖内部实现:你自定义的映射类继承了GlassFish内部的
org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper,这是内部实现类,不应直接依赖,需改用Jakarta规范提供的接口。 - 实体无命名空间关联:你的实体类未绑定任何命名空间,导致编组时不会触发前缀映射逻辑。
具体修改步骤
1. 修正自定义NamespacePrefixMapper类
改用Jakarta规范的Marshaller.NamespacePrefixMapper接口,代码如下:
package io.test.convert; import jakarta.xml.bind.Marshaller; import java.util.HashMap; import java.util.Map; public class CustomNamespacePrefixMapper implements Marshaller.NamespacePrefixMapper { public static final Map<String, String> NAMESPACE_MAP = Map.of( "http://www.w3.org/2001/XMLSchema-instance", "xsi", "https://example1.com/", "example1", "https://example2.com/", "example2", "https://test.com/", "test", "https://test2.com/", "test2"); private Map<String, String> namespaceMap; public CustomNamespacePrefixMapper(final Map<String, String> namespaceMap) { this.namespaceMap = namespaceMap; } public CustomNamespacePrefixMapper() { this(new HashMap<>(NAMESPACE_MAP)); } @Override public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { return namespaceMap.getOrDefault(namespaceUri, suggestion); } }
2. 给实体类关联命名空间
推荐在包级别配置默认命名空间,在io.test.convert包下创建package-info.java:
@XmlSchema( namespace = "https://example1.com/", elementFormDefault = XmlNsForm.QUALIFIED, xmlns = { @XmlNs(prefix = "example1", namespaceURI = "https://example1.com/"), @XmlNs(prefix = "test", namespaceURI = "https://test.com/"), @XmlNs(prefix = "test2", namespaceURI = "https://test2.com/") } ) package io.test.convert; import jakarta.xml.bind.annotation.XmlNs; import jakarta.xml.bind.annotation.XmlNsForm; import jakarta.xml.bind.annotation.XmlSchema;
也可以直接在类级别指定,比如修改Child1的@XmlRootElement:
@XmlRootElement(name = "Child1", namespace = "https://example1.com/")
3. 修正JAXBContext与Marshaller配置
JAXBContext无需设置前缀映射属性,仅需在Marshaller上用正确属性名配置:
package io.test.convert; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.Marshaller; public class MainXML { public static void main(String[] args) throws Exception { // 直接创建JAXBContext,无需传递前缀映射属性 final JAXBContext jaxbContext = JAXBContext.newInstance("io.test.convert", Thread.currentThread().getContextClassLoader()); final Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); // 使用Jakarta规范的属性名设置前缀映射 marshaller.setProperty(Marshaller.NAMESPACE_PREFIX_MAPPER, new CustomNamespacePrefixMapper()); final Child1 child1 = new Child1(); child1.setName("Batman"); child1.setAge("30"); child1.setType("Superhero"); child1.setOriginalName("Bruce Wayne"); marshaller.marshal(child1, System.out); } }
4. 优化依赖配置
可移除jaxb-core依赖,jaxb-runtime已包含核心功能:
<dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-xjc</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>4.0.1</version> </dependency> </dependencies>
修改后效果
编组生成的XML会自动带上配置的命名空间前缀,示例输出:
<example1:Child1 xmlns:example1="https://example1.com/" xmlns:test="https://test.com/" xmlns:test2="https://test2.com/"> <example1:name>Batman</example1:name> <example1:age>30</example1:age> <example1:originalName>Bruce Wayne</example1:originalName> </example1:Child1>
内容的提问来源于stack exchange,提问作者BATMAN_2008
相关产品推荐
相关产品推荐

