在 JAXB 编组时删除命名空间前缀

Posted

技术标签:

【中文标题】在 JAXB 编组时删除命名空间前缀【英文标题】:Remove namespace prefix while JAXB marshalling 【发布时间】:2013-06-20 20:12:09 【问题描述】:

我有从模式创建的 JAXB 对象。在编组时,xml 元素正在使用 ns2 进行注释。对于这个问题,我已经尝试了网络上存在的所有选项,但它们都不起作用。我无法修改我的架构或更改 package-info.java。请帮忙

【问题讨论】:

这里提到的另一个解决方案 [***.com/a/29945934/4745777][1] [1]: ***.com/a/29945934/4745777 【参考方案1】:

经过大量研究和修补,我终于设法解决了这个问题。请接受我的歉意,因为我没有发布指向原始参考的链接 - 有很多而且我没有做笔记 - 但this 一个肯定有用。

我的解决方案使用过滤XMLStreamWriter,它应用一个空的命名空间上下文。

public class NoNamesWriter extends DelegatingXMLStreamWriter 

  private static final NamespaceContext emptyNamespaceContext = new NamespaceContext() 

    @Override
    public String getNamespaceURI(String prefix) 
      return "";
    

    @Override
    public String getPrefix(String namespaceURI) 
      return "";
    

    @Override
    public Iterator getPrefixes(String namespaceURI) 
      return null;
    

  ;

  public static XMLStreamWriter filter(Writer writer) throws XMLStreamException 
    return new NoNamesWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(writer));
  

  public NoNamesWriter(XMLStreamWriter writer) 
    super(writer);
  

  @Override
  public NamespaceContext getNamespaceContext() 
    return emptyNamespaceContext;
  


你可以找到DelegatingXMLStreamWriterhere。

然后您可以使用以下命令过滤编组 xml:

  // Filter the output to remove namespaces.
  m.marshal(it, NoNamesWriter.filter(writer));

我确信有更有效的机制,但我知道这个可行。

【讨论】:

如果您不想使用 cxf,请导入 jaxws-rtxxx.jar,然后使用标准的 jawx-ws 通过以下代码执行相同的技巧:class NoNamesWriter extends XMLStreamWriterFilter,【参考方案2】:

对我来说,只更改 package-info.java 类就像一个魅力,正如 zatziky 所说:

包信息.java

 @javax.xml.bind.annotation.XmlSchema
 (namespace = "http://example.com",
 xmlns = @XmlNs(prefix = "", namespaceURI = "http://example.com"),
 elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)

package my.package;
import javax.xml.bind.annotation.XmlNs;

【讨论】:

如果您使用 xjc 通过 .xjb 文件从 xsd 生成 jaxb 工件,则会生成 package-info.java,您不应手动更改它。如果你使用 Maven 运行 xjc,你可以使用 maven-jaxb2-plugi 和 github.com/Siggen/jaxb2-namespace-prefix。这允许将命名空间前缀添加到 .xjb 文件(这是 xjc 的输入以自定义代码的生成方式)。【参考方案3】:

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


包信息.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;

【讨论】:

你救了我的命【参考方案4】:

您可以使用NamespacePrefixMapper 扩展来控制您的用例的命名空间前缀。 JAXB 参考实现和 EclipseLink JAXB (MOXy) 都支持相同的扩展。

http://wiki.eclipse.org/EclipseLink/Release/2.4.0/JAXB_RI_Extensions/Namespace_Prefix_Mapper

【讨论】:

您好,我最近一直在关注您对我正在构建 XML 的项目的回答。我正在使用Moxy 使用Marshalling 方法创建XML。出于某种原因,即使我在创建 QName 时提供了自己的自定义前缀,生成的 XML 也会采用默认命名空间,例如 ns0, ns1, etc。您能否看看这个问题并提出您的建议:***.com/questions/67553941/…【参考方案5】:

每个解决方案都需要复杂的覆盖或注释,这似乎不适用于最新版本。我使用一种更简单的方法,只是替换烦人的命名空间。我希望 Google & Co 使用 JSON 并摆脱 XML。

kml.marshal(file);

String kmlContent = FileUtils.readFileToString(file, "UTF-8");
kmlContent = kmlContent.replaceAll("ns2:","").replace("<kml xmlns:ns2=\"http://www.opengis.net/kml/2.2\" xmlns:ns3=\"http://www.w3.org/2005/Atom\" xmlns:ns4=\"urn:oasis:names:tc:ciq:xsdschema:xAL:2.0\" xmlns:ns5=\"http://www.google.com/kml/ext/2.2\">", "<kml>");
FileUtils.write(file, kmlContent, "UTF-8");

【讨论】:

以上是关于在 JAXB 编组时删除命名空间前缀的主要内容,如果未能解决你的问题,请参考以下文章

使用命名空间和前缀的 JAXB 解组

JAXB:需要所有元素的命名空间前缀

JAXB:为啥在生成的 xml 文档中未使用定义的命名空间前缀?

如果命名空间声明在 SOAP 信封上,如何使用 JAXB 解组 SOAP 响应?

如何获取JAXB对象的命名空间

在不使用 NamespacePrefixMapper 的情况下定义 Spring JAXB 命名空间