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

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

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

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

当前:

          

通缉:

          

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

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

不是很漂亮但你可以在根元素中添加一个空的schemaLocation:

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

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

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

     ...   ...   ...   

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。

    true  

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

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

PreferredMapper类中的getPreferredPrefix()方法返回首选前缀,在本例中, mappedNamespacea将在编组XML的根元素处声明。

    true  

如果您正在使用Maven,那么只需将其添加到您的pom:

  com.sun.xml.bind jaxb-impl 2.2.2 jar compile  

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

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

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

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

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

所有者: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”):

  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 namespaces = new ArrayList(); 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; 

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

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

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