Remove namespace prefix while JAXB marshalling

73,162

Solution 1

After much research and tinkering I have finally managed to achieve a solution to this problem. Please accept my apologies for not posting links to the original references - there are many and I wasn't taking notes - but this one was certainly useful.

My solution uses a filtering XMLStreamWriter which applies an empty namespace context.

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

}

You can find a DelegatingXMLStreamWriter here.

You can then filter the marshalling xml with:

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

I am sure there are more efficient mechanisms but I know this one works.

Solution 2

For me, only changing the package-info.java class worked like a charm, exactly as zatziky stated :

package-info.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;

Solution 3

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:

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"):

            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;

Solution 4

You can use the NamespacePrefixMapper extension to control the namespace prefixes for your use case. The same extension is supported by both the JAXB reference implementation and EclipseLink JAXB (MOXy).

Share:
73,162

Related videos on Youtube

user2487308
Author by

user2487308

Updated on January 25, 2021

Comments

  • user2487308
    user2487308 over 3 years

    I have JAXB objects created from a schema. While marshalling, the xml elements are getting annotated with ns2. I have tried all the options that exist over the net for this problem, but none of them works. I cannot modify my schema or change package-info.java. Please help

  • Junchen Liu
    Junchen Liu over 8 years
    in case you don't want to use cxf, import the jaxws-rtxxx.jar then using the standard jawx-ws does the same trick by code the following: class NoNamesWriter extends XMLStreamWriterFilter,
  • Estevão Jordão
    Estevão Jordão about 6 years
    you save my life
  • Henno Vermeulen
    Henno Vermeulen almost 6 years
    If you use xjc to generate jaxb artifacts from an xsd through a .xjb file, the package-info.java is genereated and you should not manually change it. If you use Maven to run xjc, you can use the the maven-jaxb2-plugi together with the github.com/Siggen/jaxb2-namespace-prefix. This allows to add namespace prefixes to the .xjb file (which is the input to xjc to customize how code is generated).
  • BATMAN_2008
    BATMAN_2008 about 3 years
    Hi, I have been following your answers lately for my project which is building XML. I am using the Moxy to create XML using the Marshalling approach. For some reason, the generated XML takes the default namespaces such as ns0, ns1, etc even though I have provided my own custom prefix while creating the QName. Can you please have a look at this question and provide your suggestion: stackoverflow.com/questions/67553941/…