27
votes

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

5

5 Answers

22
votes

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.

13
votes

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;
5
votes

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;
2
votes

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

0
votes

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