JAXB and Namespace Prefixes
Join the DZone community and get the full member experience.
Join For FreeIn a previous post I covered how to use namespace qualification with JAXB. In this post I will cover how to control the prefixes that are used. This is not covered in the JAXB (JSR-222) specification but I will demonstrate the extensions available in both the reference and EclipseLink MOXy implementations for handling this use case
Java Model
The following domain model will be used for this post. The @XmlRootElement and @XmlElement annotation are used to specify the appropriate namespace qualification.
package blog.prefix; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(namespace="http://www.example.com/FOO") public class Root { private String a; private String b; private String c; @XmlElement(namespace="http://www.example.com/BAR") public String getA() { return a; } public void setA(String a) { this.a = a; } @XmlElement(namespace="http://www.example.com/FOO") public String getB() { return b; } public void setB(String b) { this.b = b; } @XmlElement(namespace="http://www.example.com/OTHER") public String getC() { return c; } public void setC(String c) { this.c = c; } }
Demo Code
We will use the following code to populate the domain model and produce the XML.
package blog.prefix; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; public class Demo { public static void main(String[] args) throws Exception { JAXBContext ctx = JAXBContext.newInstance(Root.class); Root root = new Root(); root.setA("A"); root.setB("B"); root.setC("OTHER"); Marshaller m = ctx.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); m.marshal(root, System.out); } }
Output
XML like the following is produced by default. The JAXB implementation has arbitrarily assigned prefixes to the namespace URIs specified in the domain model:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ns2:root xmlns="http://www.example.com/BAR" xmlns:ns2="http://www.example.com/FOO" xmlns:ns3="http://www.example.com/OTHER"> <a>A</a> <ns2:b>B</ns2:b> <ns3:c>OTHER</ns3:c> </ns2:root>
Specify Prefix Mappings with JAXB RI & Metro JAXB
The reference and Metro implementations of JAXB provide a mechanism called NamespacePrefixMapper to control the prefixes that will be assigned to namespaces.
NamespacePrefixMapper
package blog.prefix; import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper; //import com.sun.xml.bind.marshaller.NamespacePrefixMapper; public class MyNamespaceMapper extends NamespacePrefixMapper { private static final String FOO_PREFIX = ""; // DEFAULT NAMESPACE private static final String FOO_URI = "http://www.example.com/FOO"; private static final String BAR_PREFIX = "bar"; private static final String BAR_URI = "http://www.example.com/BAR"; @Override public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { if(FOO_URI.equals(namespaceUri)) { return FOO_PREFIX; } else if(BAR_URI.equals(namespaceUri)) { return BAR_PREFIX; } return suggestion; } @Override public String[] getPreDeclaredNamespaceUris() { return new String[] { FOO_URI, BAR_URI }; } }
Demo Code
The NamespacePrefixMapper is set on an instance of Marshaller. I would recommend wrapping the setPropery call in a try/catch block so that your application does not fail if you change JAXB implementations.
package blog.prefix; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; public class Demo { public static void main(String[] args) throws Exception { JAXBContext ctx = JAXBContext.newInstance(Root.class); Root root = new Root(); root.setA("A"); root.setB("B"); root.setC("OTHER"); Marshaller m = ctx.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); try { m.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", new MyNamespaceMapper()); //m.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespaceMapper()); } catch(PropertyException e) { // In case another JAXB implementation is used } m.marshal(root, System.out); } }
Output
The resulting document now uses the NamespacePrefixMapper to determine the prefixes that should be used in the resulting document.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <root xmlns:bar="http://www.example.com/BAR" xmlns="http://www.example.com/FOO" xmlns:ns3="http://www.example.com/OTHER"> <bar:a>A</bar:a> <b>B</b> <ns3:c>OTHER</ns3:c> </root>
Specify Prefix Mappings with EclipseLink JAXB (MOXy)
MOXy will use the namespace prefixes as they are defined on the @XmlSchema annotation. In order for MOXy to be able to use the default namespace the elementFormDefault property on the @XmlSchema annotation must be set to XmlNsForm.QUALIFIED.
package-info
XmlSchema( elementFormDefault=XmlNsForm.QUALIFIED, namespace="http://www.example.com/FOO", xmlns={@XmlNs(prefix="bar", namespaceURI="http://www.example.com/BAR")} ) package blog.prefix; import javax.xml.bind.annotation.XmlNs; import javax.xml.bind.annotation.XmlNsForm; import javax.xml.bind.annotation.XmlSchema;
Output
The resulting document now uses the xmlns setting from the @XmlSchema annotation to determine the prefixes that should be used in the resulting document.
<?xml version="1.0" encoding="UTF-8"?> <root xmlns="http://www.example.com/FOO" xmlns:ns0="http://www.example.com/OTHER" xmlns:bar="http://www.example.com/BAR"> <bar:a>A</bar:a> <b>B</b> <ns0:c>OTHER</ns0:c> </root>
Further Reading
If you enjoyed this post then you may also be interested in:
From http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html
Opinions expressed by DZone contributors are their own.
Comments