当使用JAXB将对象转换为字符串时,是否可以自定义命名空间前缀?

30
例如,我有一个简单的模式,它导入另一个模式。第二个模式(urn:just:attributes,just-attributes.xsd)仅定义了一个属性组。
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.example.org/MySchema"
    xmlns:tns="http://www.example.org/MySchema" 
    elementFormDefault="qualified"
    xmlns:ja="urn:just:attributes">

    <import schemaLocation="just-attributes.xsd" namespace="urn:just:attributes"/>

    <element name="MyElement">
        <complexType>
            <attributeGroup ref="ja:AttributeGroup"/>
        </complexType>
    </element>
</schema>

我正在使用Metro xjc Ant任务从此架构生成类。 我遇到的问题是,我正在与第三方应用程序交互时,命名空间非常奇怪。 在这种情况下,我需要一个字符串值,因此我必须对其进行序列化。 我使用样板代码来实现。

private static <T> String marshal(T object) throws JAXBException{
    OutputStream outputStream = new ByteArrayOutputStream();
    JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass());
    Marshaller marshaller = jaxbContext.createMarshaller();
    marshaller.marshal(object, outputStream);
    return outputStream.toString();
}

这让我得到了一个类似的东西。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:MyElement xmlns:ns1="urn:just:attributes" xmlns:ns2="http://www.example.org/MySchema" ns1:attrib1="1234" ns1:attrib2="5678"/>

我遇到的问题是,第三方期望类似于xmlns:thirdpartyns="urn:just:attributes"这样的东西,也就是说,他们是基于命名空间给定的名称进行解析。他们的软件必须要使用"thirdpartyns"才能正常工作。
除了在生成的字符串中执行查找和替换之外,有没有其他方法可以解决这个问题?也许可以使用自定义绑定规则?
4个回答

28

这篇文章展示了如何进行操作。

另一篇文章: http://www.systemmobile.com/?p=280

如果链接失效,以下是关键内容:

在 com.sun.xml.bind.marshaller 包中找到 NamespacePrefixMapper 类。这个抽象类只有一个需要实现的方法:

public abstract String getPreferredPrefix(  
     String namespaceUri,         
     String suggestion,         
     boolean requirePrefix); 
< p > 那么

Marshaller marshaller =        
    jaxbContext.createMarshaller();        
marshaller.setProperty(”com.sun.xml.bind.namespacePrefixMapper”,        
    new MyNamespacePrefixMapper());  

如果您也在使用javax.xml.xpath.XPath,您的NamespacePrefixMapper也可以实现javax.xml.namespace.NamespaceContext,在单个类中集中您的命名空间定制。


链接已失效... @DaveC 请检查一下! - basZero
谢谢,第一个链接对我帮助很大。 - Aaron
1
这仅适用于Java SE5 而不适用于SE6,对于SE6,请参见我的评论:https://dev59.com/cnI-5IYBdhLWcg3wMFIf#4658093 - basZero
第一个链接的解决方案2非常简单,而且效果很好。谢谢! - GreenTurtle
自从JAXB 3版本以后,正确的属性是org.glassfish.jaxb.namespacePrefixMapper - undefined

12

我在Java SE6中测试了那个,与Java SE 5的解决方案(如上述描述)相比需要进行一些小的更改:

    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
    m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
    m.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", mapper);

因此,与Java SE5版本相比,上述第三个属性在包名称中包含额外的.internal。 我尚未发现如何告诉Marshaller哪个命名空间URI成为默认命名空间(“”)。如果我重写getPreferredPrefix()方法并返回空字符串,则Marshaller在编写默认命名空间的属性时会出现问题(在这种情况下,它会创建一个名为ns1的新命名空间)


3
显然在Java SE 7中,它再次使用“com.sun.xml.bind.namespacePrefixMapper”...(使用Java SE 6属性时我会得到一个异常)。 - edbras
3
有人知道如何在Java SE 8中正确实现吗?@edbras - basZero

11

我有同样的问题。在package-info.java文件中(如果你没有该文件,可以手动创建),添加xmlns部分:

@javax.xml.bind.annotation.XmlSchema(xmlns = {
        @javax.xml.bind.annotation.XmlNs(namespaceURI = "urn:just:attributes", prefix = "thirdpartyns") }, 
        elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)

6
如果我从XSD生成源代码,使用Apache Maven插件,我该如何进行自定义设置?请指导。 - Dmitry Avgustis

0

有一种方法可以做到这一点,它使用了一个名为NamespacePrefixMapper的内部JAXB实现类。在JAXB RI中,它位于com.sun.xml.bind.marshaller,但在Java6中,它位于com.sun.xml.internal.bind.marshaller

这是一个抽象类,您可以对其进行子类化并实现将命名空间URI映射到前缀的抽象方法。

然后,您将该子类的实例注入到marshaller中:

JAXBContext context = ...
Marshaller marshaller = context.createMarshaller();
NamespacePrefixMapper prefixMapper = new MyPrefixMapperImpl();
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", prefixMapper);

Java6版本的属性名称可能会有所不同,但您可以理解这个概念。

请注意,这是一个内部的JAXB实现类,因此不能保证在未来的版本中仍然存在。


1
请不要对“internal”类进行子类化。 - Barett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接