Search code examples
xmlnamespacesjaxbmoxyjaxb2

JAXB and namespaces related issue


I have the follow XML after marshall a JAXB class:

<?xml version="1.0" encoding="UTF-8"?>
<foo:Object xsi:schemaLocation="http://foo.com/ http://foo.com/foo.xsd" 
xmlns:ns0="http://lipsum.com/bar" xmlns:foo="http://foo.com/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <foo:Some attr1="601"/>
   <foo:Others>
      <bar:Another xsi:schemaLocation="http://lipsum.com/bar 
      http://lipsum.com/bar.xsd" xmlns:bar="http://lipsum.com/bar">
         <bar:SomeOther attr2="01"/>
      </bar:Another>
  </foo:Others>
</foo:Object>

The <foo:Others/> element in the jaxb class are declared as:

    @XmlAnyElement(lax = true)
    @XmlElementRefs({
         @XmlElementRef(name="Another", type=Another.class, required=false)
    })
    protected List<Object> any;

It may contain an unknow number of other elements with their own namespaces.

The problem:

When i marshall the Some.class object, jaxb put the namespace of the Another.class into the root element as ns0, since i get the <bar:Another> marshalled as Element from another method with their namespace included i don't need it again into the root element.

That is a problem since <bar:Another> is not required, so if i marshall the Some.class object with an empty list i'am goin to have always the xmlns:ns0.

I need the @XmlElementRefs and @XmlElementRef annotations because i need to convert from json to XML and Jackson need to know the type that "any" list could have.

How can i tell to JAXB Oracle/Moxy implementation that:

1.- Ignore the namespaces from @XmlElementRef classes during the marshalling.
2.- Remove the not used namespaces aka NS[0-9] prefixes from the root element.

Any help would be apreciated.


Solution

  • Just in case some one needs this i got a way to remove the unwanted namespaces, the trick is provided from XMLStreamWriter

    Step 1.- Create an WrapperXMLStreamWriter class that goin to wrap a XMLStreamWriter.

    public class WrapperXMLStreamWriter implements XMLStreamWriter {
        private final XMLStreamWriter writer;
    
        public WrapperXMLStreamWriter(XMLStreamWriter writer) {
            this.writer = writer;
        }
    
        /* (non-Javadoc)
         * @see javax.xml.stream.XMLStreamWriter#writeStartElement(java.lang.String)
         */
        @Override
        public void writeStartElement(String localName) throws XMLStreamException {
            // for each overloaded method call to writer do the method
            writer.writeStartElement(localName);
        }
    
         /* (non-Javadoc)
         * @see javax.xml.stream.XMLStreamWriter#writeNamespace(java.lang.String, java.lang.String)
         */
        @Override
        public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
            // Here is the trick
            if(!prefix.startsWith("ns")){
                writer.writeNamespace(prefix, namespaceURI);
            }
        }
    
        ...
    }
    

    Step 2.- Crate a marshaller as usual

    Map<String,String> prefixes = new HashMap<String, String>();
    
    // 
    prefixes.put("http://www.w3.org/2001/XMLSchema-instance","xsi");
    prefixes.put("http://foo.com/", "foo");
    
    Marshaller marshaller =  AppContextTool.getInstance().getContextForFoo().createMarshaller();
    
    // 
    marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapperImpl(prefixes));
    marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://foo.com/ http://foo.com/foo.xsd");
    

    3.- Marshall the Foo instance with the given XMLStreamWriter of the first step

    Foo foo = new Foo();
    
    ByteArrayOutputStream xml= new ByteArrayOutputStream();
    try {
        XMLStreamWriter writer = 
                    new WrapperXMLStreamWriter((XMLStreamWriter) XMLOutputFactory.newInstance().createXMLStreamWriter(xml, "UTF-8"));
    
        marshaller.marshal(foo, writer);
    } catch (XMLStreamException | FactoryConfigurationError e) {
        e.printStackTrace();
    }
    

    4.- The result:

    <?xml version="1.0" encoding="UTF-8"?>
    <foo:Object xsi:schemaLocation="http://foo.com/ http://foo.com/foo.xsd" 
     xmlns:foo="http://foo.com/" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <foo:Some attr1="601"/>
            <foo:Others>
                <bar:Another xsi:schemaLocation="http://lipsum.com/bar 
      http://lipsum.com/bar.xsd" xmlns:bar="http://lipsum.com/bar">
              <bar:SomeOther attr2="01"/>
           </bar:Another>
      </foo:Others>
      </foo:Object>
    

    No more NS namespaces, Hope this helps someone.