29

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

user2487308
  • 337
  • 1
  • 5
  • 9
  • Another solution mentioned here [http://stackoverflow.com/a/29945934/4745777][1] [1]: http://stackoverflow.com/a/29945934/4745777 – HamedKhan Apr 29 '15 at 14:04

5 Answers5

22

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.

Community
  • 1
  • 1
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • 3
    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, – Junchen Liu Dec 10 '15 at 15:00
15

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;
cata2d
  • 151
  • 1
  • 4
  • 2
    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 https://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). – Henno Vermeulen Aug 16 '18 at 12:42
  • This is the cleanest solution for me as long you don't need to mix different element from different namespaces. This is always my preferred way than writing a lot of code. – рüффп Jul 22 '22 at 07:00
5

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;
Lii
  • 11,553
  • 8
  • 64
  • 88
Amio.io
  • 20,677
  • 15
  • 82
  • 117
2

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

bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • 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: https://stackoverflow.com/questions/67553941/creation-of-qname-takes-default-namesapce-prefix-rather-than-the-provided-parame – BATMAN_2008 May 16 '21 at 12:13
0

Every solution requires complex overwriting or annotations which seems not to work with recent version. I use a simpler approach, just by replacing the annoying namespaces. I wish Google & Co would use JSON and get rid of 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");
Brain
  • 447
  • 7
  • 21