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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2252028/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 05:15:00  来源:igfitidea点击:

JAXB: How to avoid repeated namespace definition for xmlns:xsi

javaxmljaxb

提问by sfussenegger

I have a JAXB setup where I use a @XmlJavaTypeAdapter to replace objects of type Personwith objects of type PersonRefthat only contains the person's UUID. This works perfectly fine. However, the generated XML redeclares the same namespace (xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance") every time it's used. While this is generally okay, it just doesn't feel right.

我有一个 JAXB 设置,我使用 @XmlJavaTypeAdapter 将类型Person的对象替换为PersonRef仅包含个人 UUID的类型的对象。这工作得很好。但是,xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"每次使用生成的 XML 时都会重新声明相同的命名空间 ( )。虽然这通常没问题,但感觉不对。

How can I configure JAXB to declare xmlns:xsi at the very beginning of the document? Can I manually add namespace declarations to the root element?

如何配置 JAXB 以在文档的最开头声明 xmlns:xsi?我可以手动向根元素添加命名空间声明吗?

Here's an example of what I want to achive:

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

Current:

当前的:

<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>

Wanted:

通缉:

<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>

采纳答案by Dany

You can do it with the code:

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

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;

                }
            });

回答by java.is.for.desktop

It's XML, so you could process the output using DOM or XSLT to get rid of multiple namespace references.

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

回答by VonC

It looks like a JAXB customization Namespace mapper issue

看起来像是JAXB 自定义命名空间映射器问题

When you marshall an XML document using JAXB 1.0, a Marshaller object, a JAXB object that controls the process of marshalling, provides namespace declarations in the resulting XML document. Sometimes the Marshaller produces a lot of namespace declarations that look redundant, for example:

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

   <?xml version="1.0"?>
   <root>
      <ns1:element xmlns:ns1="urn:foo"> ... </ns1:element>
      <ns2:element xmlns:ns2="urn:foo"> ... </ns2:element>
      <ns3:element xmlns:ns3="urn:foo"> ... </ns3:element>
   </root>

JAXB 2.0 changes this behavior. If you use JAXB 2.0 (or later) to marshal an XML document, the Marshaller declares all statically known namespace Uniform Resource Identifiers (URIs), that is, those URIs that are used as element or attribute names in JAXB annotations.

JAXB may also declare additional namespaces in the middle of an XML document, for example when a qualified name (QName) that is used as an attribute or element value requires a new namespace URI, or when a Document Object Model (DOM) node in a content tree requires a new namespace URI. This behavior might produce an XML document that has a lot of namespace declarations with automatically-generated namespace prefixes.

The problem is that automatically-generated namespace prefixes such as ns1, ns2, and ns3, are not user friendly -- they typically do not help people understand the marshalled XML.

Fortunately, JAXB 2.0 (or later) provides a service provider interface (SPI) named com.sun.xml.bind.marshaller.NamespacePrefixMapperthat you can use to specify more helpful namespace prefixes for marshalling.

When the JAXBSample program marshalls the XML document the first time, it does it without using a NamespacePrefixMapperclass. As a result, the Marshaller automatically generates a namespace prefix, in this case, ns2.

JAXB 2.0 改变了这种行为。如果您使用 JAXB 2.0(或更高版本)来编组 XML 文档,则编组器会声明所有静态已知的命名空间统一资源标识符 (URI),即在 JAXB 注释中用作元素或属性名称的那些 URI。

JAXB 还可以在 XML 文档中间声明额外的命名空间,例如当QName用作属性或元素值的限定名 ( ) 需要新的命名空间 URI 时,或者当内容中的文档对象模型 (DOM) 节点时tree 需要一个新的命名空间 URI。此行为可能会生成一个 XML 文档,其中包含大量带有自动生成的名称空间前缀的名称空间声明。

问题是自动生成的名称空间前缀,如 ns1、ns2 和 ns3,对用户不友好——它们通常不能帮助人们理解编组的 XML。

幸运的是,JAXB 2.0(或更高版本)提供了一个名为的服务提供者接口 (SPI) com.sun.xml.bind.marshaller.NamespacePrefixMapper,您可以使用它来指定更有用的名称空间前缀以进行编组。

当 JAXBSample 程序第一次编组 XML 文档时,它不使用NamespacePrefixMapper类。因此,Marshaller 会自动生成命名空间前缀,在本例中为 ns2。

   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   <ns2:JustAnElement xmlns:ns2="a">
       <foo>true</foo>
   </ns2:JustAnElement>

Example of a configuration avoiding the namespace repetition:

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

The second marshalling done by the JAXBSampleprogram uses a NamespacePrefixMapperclass as follows:

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

   NamespacePrefixMapper m = new PreferredMapper();
               marshal(jc, e, m);

   public static class PreferredMapper extends NamespacePrefixMapper {
           @Override
           public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
               return "mappedNamespace" + namespaceUri;
           }
       }

The getPreferredPrefix()method in the PreferredMapperclass returns the preferred prefix, in this case, mappedNamespaceato be declared at the root element of the marshalled XML.

类中的getPreferredPrefix()方法PreferredMapper返回首选前缀,在这种情况下,mappedNamespacea将在编组 XML 的根元素中声明。

   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   <mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a">
       <foo>true</foo>
   </mappedNamespacea:JustAnElement>

回答by msp

Not thatpretty but you could add an empty schemaLocation to the root element:

不是漂亮,但你可以一个空的schemaLocation添加到根元素:

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

回答by Miguel de Melo

if you're using Maven then just add this to your pom:

如果您使用的是 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>

no need for PreferredMapper if you configure your annotations as defined in the example above. Although I have a package-info.jave file confugures as follows:

如果您按照上面示例中的定义配置注释,则不需要 PreferredMapper。虽然我有一个 package-info.jave 文件配置如下:

@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;

回答by Aguid

This is the best answer I find it in the web.

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

The xsi:typedeclarations are most likely being created because the declared type of the JAXBElementdoes not match the type of the value.

xsi:type声明最有可能被创建因为声明的类型JAXBElement不匹配值的类型。

If the ObjectFactoryhas a create method for the correct JAXBElementyou should use that since it should correctly populate both the QNameand the type info; otherwise I would try setting the declared type (second constructor arg) of the JAXBElementto String.class(assuming this is the type of commentTest) instead of CommentType.Comment.

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

Source: http://www.java.net/forum/topic/glassfish/metro-and-jaxb/how-do-i-remove-namespace-declarations-child-elements

来源:http: //www.java.net/forum/topic/glassfish/metro-and-jaxb/how-do-i-remove-namespace-declarations-child-elements

Owner: cbrettin

所有者: cbrettin

回答by Amio.io

You can let the namespaces be written only once. You will need a proxy class of the XMLStreamWriter and a package-info.java. Then you will do in your code:

您可以让命名空间只写入一次。您将需要一个 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());

Proxy class (the important method is "writeNamespace"):

代理类(重要的方法是“writeNamespace”):

            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() ...

}

package-info.java:

包-info.java:

@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;

回答by Eason Xiao

Add your nsPrefixmapping by doing this:

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

marshaller.setNamespaceMapping("myns","urn:foo");

marshaller.setNamespaceMapping("myns","urn:foo");