Java JAXB:如何避免xmlns:xsi的重复命名空间定义

Java JAXB:如何避免xmlns:xsi的重复命名空间定义,java,xml,jaxb,Java,Xml,Jaxb,我有一个JAXB设置,其中我使用@XmlJavaTypeAdapter将Person类型的对象替换为只包含Person的UUID的PersonRef类型的对象。这个很好用。但是,生成的XML重新声明了相同的名称空间(xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance“)每次使用时。虽然这通常是好的,但感觉不太对劲 如何配置JAXB以在文档一开始就声明xmlns:xsi?我可以手动将名称空间声明添加到根元素吗 下面是我想要实现的一个例子: 当前

我有一个JAXB设置,其中我使用@XmlJavaTypeAdapter将
Person
类型的对象替换为只包含Person的UUID的
PersonRef
类型的对象。这个很好用。但是,生成的XML重新声明了相同的名称空间(
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance“
)每次使用时。虽然这通常是好的,但感觉不太对劲

如何配置JAXB以在文档一开始就声明xmlns:xsi?我可以手动将名称空间声明添加到根元素吗

下面是我想要实现的一个例子:

当前:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a">
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation> 
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation>
    <!-- SNIP: some more relations -->
</person>
<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0"/> 
    </relation> 
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"/> 
    </relation>
    <!-- SNIP: some more relations -->
</person>

通缉令:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a">
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation> 
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation>
    <!-- SNIP: some more relations -->
</person>
<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0"/> 
    </relation> 
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"/> 
    </relation>
    <!-- SNIP: some more relations -->
</person>

它是XML,因此您可以使用DOM或XSLT处理输出,以摆脱多个名称空间引用。

它看起来像一个

当您使用JAXB 1.0整理XML文档时,Marshaller对象(控制编组过程的JAXB对象)在生成的XML文档中提供名称空间声明。有时,封送拆收器会生成许多看起来冗余的命名空间声明,例如:

避免命名空间重复的配置示例:

JAXBSample
程序完成的第二次编组使用
NamespacePrefixMapper
类,如下所示:

@javax.xml.bind.annotation.XmlSchema(
        namespace = "mylovelynamespace1", 
        xmlns = {
                    @javax.xml.bind.annotation.XmlNs(prefix = "myns1", namespaceURI = "mylovelynamespace1"),
                    @javax.xml.bind.annotation.XmlNs(prefix = "myns2", namespaceURI = "mylovelynamespace2")
                }, 
                elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.mylovelycompanyname.package;
PreferredMapper
类中的
getPreferredPrefix()
方法返回首选前缀,在本例中,
mappedNamespacea
将在封送XML的根元素处声明


真的
没有那么漂亮,但是您可以向根元素添加一个空schemaLocation:

marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "");

您可以使用以下代码执行此操作:

marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() {
                @Override
                public String[] getPreDeclaredNamespaceUris() {
                    return new String[] { 
                        XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
                    };
                }

                @Override
                public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
                    if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI))
                        return "xsi";
                    if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI))
                        return "xs";
                    if (namespaceUri.equals(WellKnownNamespace.XML_MIME_URI))
                        return "xmime";
                    return suggestion;

                }
            });

如果您使用的是Maven,则只需将以下内容添加到pom中:

<dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.2</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>

这是我在网上找到的最好的答案

创建
xsi:type
声明很可能是因为
JAXBElement
声明的类型与值的类型不匹配

如果
ObjectFactory
具有正确的
JAXBElement
的创建方法,则应使用该方法,因为它应正确填充
QName
和类型信息;否则,我将尝试将
JAXBElement
的声明类型(第二个构造函数arg)设置为
String.class
(假设这是
commentTest
)而不是
CommentType.Comment

资料来源:

所有者:
cbrettin

您只能让名称空间写入一次。您将需要XMLStreamWriter的代理类和package-info.java。然后,您将在代码中执行以下操作:

StringWriter stringWriter = new StringWriter();
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory
                                                               .newInstance().createXMLStreamWriter(stringWriter));
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(books, writer);
System.out.println(stringWriter.toString());
代理类(重要的方法是“writeNamespace”):


通过执行以下操作添加您的
nsPrefix
映射:


marshaller.setNamespaceMapping(“myns”、“urn:foo”)

对不起,我宁愿开枪打自己的脚:)但说真的,在这个任务中使用XSLT或DOM似乎有点过激。最终,这归结为满足我对美学的渴望:)JAXB不是那么可配置的。我认为这是做这件事的唯一方法(或者让它去做)。另请看一个与此无关的问题。谢谢你的回答。默认名称空间PrefixMapper已将“”映射到“xsi”(请参阅)。所以我不认为有一种方法可以更早地使用NamespacePrefixMapper声明名称空间。不过名称空间上下文看起来很有希望。不过我还在想办法弄到手。(无论如何,你的努力+1)如果你感兴趣:3个月后,结果证明你的答案离正确不远:正确的课程,错误的方法(见Dany的答案)。我不敢相信我在检查你的建议时没有注意到getPreDeclaredNamespaceUris()方法。@sfussenegger:确实,我仍然感兴趣;)我对丹妮的答案投了赞成票。确实没有那么漂亮:)但至少它接近于正确答案。我认为它太棒了!我被迫停留在使用较旧的JAXB2.x的Java6运行时上。通过您的更改,我只在根元素上获得模式声明,而不在任何子元素上。根元素现在看起来像这样:
我使用jaxb2,这对我不起作用,抛出RuntimeException属性名称应该是com.sun.xml.internal.bind.namespacePrefixMapper
@javax.xml.bind.annotation.XmlSchema(
        namespace = "mylovelynamespace1", 
        xmlns = {
                    @javax.xml.bind.annotation.XmlNs(prefix = "myns1", namespaceURI = "mylovelynamespace1"),
                    @javax.xml.bind.annotation.XmlNs(prefix = "myns2", namespaceURI = "mylovelynamespace2")
                }, 
                elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.mylovelycompanyname.package;
StringWriter stringWriter = new StringWriter();
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory
                                                               .newInstance().createXMLStreamWriter(stringWriter));
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(books, writer);
System.out.println(stringWriter.toString());
            class WrapperXMLStreamWriter implements XMLStreamWriter {

                   private final XMLStreamWriter writer;

                   public WrapperXMLStreamWriter(XMLStreamWriter writer) {
                       this.writer = writer;
                   }

                     //keeps track of what namespaces were used so that not to 
                     //write them more than once
                   private List<String> namespaces = new ArrayList<String>();

                   public void init(){
                       namespaces.clear();
                   }

                   public void writeStartElement(String localName) throws XMLStreamException {
                       init();
                       writer.writeStartElement(localName);

                   }

                   public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
                       init();
                       writer.writeStartElement(namespaceURI, localName);
                   }

                   public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
                       init();
                       writer.writeStartElement(prefix, localName, namespaceURI);
                   }

                   public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
                       if(namespaces.contains(namespaceURI)){ 
                           return;
                       }
                       namespaces.add(namespaceURI);
                       writer.writeNamespace(prefix, namespaceURI);
                   }

    // .. other delegation method, always the same pattern: writer.method() ...

}
@XmlSchema(elementFormDefault=XmlNsForm.QUALIFIED, attributeFormDefault=XmlNsForm.UNQUALIFIED ,
        xmlns = { 
        @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")})
package your.package;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;