20 March 2011

JAXB Marshalling with Custom Namespace Prefixes

The Problem


  • You have an XML schema with multiple XML namespaces.
  • You generate a JAXB model with xjc.
  • You build a JAXB document model and use the JAXB Marshaller to create XML from the model.
  • You want to override the default namespace prefixes ns1, ns2, ... created by the Marshaller.


Solution 1


You can implement a NamespacePrefixMapper and pass it to the Marshaller

Marshaller m = context.createMarshaller();
    NamespacePrefixMapper mapper = new MyNamespacePrefixMapper();
    m.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", mapper);

Drawbacks:
  • You need to implement the Mapper.
  • The Marshaller property is implementation dependent.
  • When you use a standalone version of the JAXB Reference Implementation and not the one embedded in Oracle's JDK 6, the property name must be replaced by com.sun.xml.bind.namespacePrefixMapper.

Solution 2


Manually create the package-info.java source file which is normally generated by xjc:

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

package com.example.foo.jaxb;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;


This is enough to make the Marshaller use the prefixes defined in the @XmlNs annotations.

Now you only need to make sure that xjc does not overwrite or duplicate the package-info.java source file by setting the -npa option.

Working with Maven and the maven-jaxb2-plugin, you simply put your customized package-info.java under src/main/java and add the -npa option to the plugin configuration:

<plugin>
        <groupId>org.jvnet.jaxb2.maven2</groupId>
        <artifactId>maven-jaxb2-plugin</artifactId>
        <version>0.7.5</version>
        <executions>
          <execution>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <extension>true</extension>
          <args>
            <arg>-npa</arg>
          </args>
        </configuration>
      </plugin>

Update 21 March 2011: This approach does not work with the JAXB version contained in Oracle's JDK 1.6.0_24. You have to use a more recent version of the JAXB reference implementation by including the following dependency:

<dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-impl</artifactId>
      <version>2.2.2</version>
    </dependency>

14 comments:

Unknown said...

Will this work on WebServices as well? I tried manually modifying my package-info.java file and it doesn't effect the SOAP response.

Harald Wellmann said...

I haven't tried this with web services, but I don't see why it shouldn't work as long as you use the proper version of JAXB. It doesn't work with the 2.1 version incorporated in JDK 1.6.0.

danLeon said...

Solution 2 work in JDK 7

Anil said...

I did not have JDK 7 but when i added JAXB 2.2 as mentioned in the post, It worked. Thanks a lot

Anil said...

I was already using package-info.java but it was not adding prefix.
I did not have JDK 7 too, but adding jaxb-impl in pom.xml file solved my issue. Thanks a lot.

Mr Koding said...

Thanks! Helped me a lot.

Unknown said...

Have a look at the namespace-prefix plugin (http://java.net/projects/jaxb2-commons/pages/Namespace-prefix) which implements solution #2 in an automated way.

Unknown said...

Great, especially the Ant solution

Sunny said...

Perfect ! it works ....

Ashish Desai said...

This is amazing, finally saved me after hours and hours of exploration. Great work. Thanks a lot

David Lucek said...

Thanks a lot, worked like a charm.

Unknown said...

Thank a lot it works!!!!!!!

Vinicius Monteiro said...

Hello, I am trying to build a bottom up WS using JAX-WS. wsgen generates the wsdl from the annotated classes.
I would like to remove any prefix, even if possible also the namespace in the request and response.
I tried several things, but just can't get it to work..
Thanks

Binh Nguyen said...

Thanks, nice tips