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

问题描述 投票:0回答:8

我有一个 JAXB 设置,其中使用 @XmlJavaTypeAdapter 将

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>
java xml jaxb
8个回答
17
投票

不是that漂亮,但您可以向根元素添加一个空的 schemaLocation:

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

9
投票

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

当您使用 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 改变了这种行为。如果您使用 JAXB 2.0(或更高版本)编组 XML 文档,Marshaller 会声明所有静态已知的名称空间统一资源标识符 (URI),即在 JAXB 注释中用作元素或属性名称的 URI。

JAXB 还可以在 XML 文档中间声明其他命名空间,例如,当用作属性或元素值的限定名称 (

QName

) 需要新的命名空间 URI 时,或者当文档对象模型 (DOM) 时内容树中的节点需要新的命名空间 URI。此行为可能会生成一个 XML 文档,其中包含大量带有自动生成的命名空间前缀的命名空间声明。

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

幸运的是,JAXB 2.0(或更高版本)提供了一个名为

com.sun.xml.bind.marshaller.NamespacePrefixMapper

 的服务提供者接口 (SPI),您可以使用它来指定更有用的编组命名空间前缀。

当 JAXBSample 程序第一次编组 XML 文档时,它不使用

NamespacePrefixMapper

 类。结果,Marshaller 自动生成命名空间前缀,在本例中为 ns2。

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

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

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

getPreferredPrefix()

 类中的 
PreferredMapper
 方法返回首选前缀,在本例中为要在编组 XML 的根元素处声明的 
mappedNamespacea

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

    

6
投票
您可以使用以下代码来完成:

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; } });
    

4
投票
如果你使用 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>

如果您按照上面示例中的定义配置注释,则不需要 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;
    

2
投票
这是我在网上找到的最好答案。

最有可能创建

xsi:type

 声明,因为 
JAXBElement
 的声明类型与值的类型不匹配。

如果

ObjectFactory

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

来源:

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

业主: 克布雷汀


2
投票
您可以让命名空间只写入一次。您将需要 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”):

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:

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

0
投票
它是 XML,因此您可以使用 DOM 或 XSLT 处理输出,以消除多个名称空间引用。


-1
投票
通过执行以下操作添加您的

nsPrefix

 映射:

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


    

© www.soinside.com 2019 - 2024. All rights reserved.