Thursday, October 03, 2013

JAXB namespace configuration

In this blog post I would like to demonstrate how to configure a namespace in the XML messages used by REST endpoints. If you would rather jump right to a working example, have a look on my github account. Let's consider the following example message, where widgets can have different type of resources.

<?xml version="1.0" encoding="UTF-8"?>
<bb:widget xmlns:bb="http://www.backbase.com/ns/widgets">
<bb:resources>
<bb:resource type="text/css" src="css/wrap-layout.css"/>
<bb:resource type="text/javascript" src="template/wrap-layout.js"/>
<bb:resource type="image/png" src="png/wrap-layout.js"/>
</bb:resources>
</bb:widget>
In the above example all XML elements belong to the "http://www.backbase.com/ns/widgets" namespace. This simple widget configuration message could have the following XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema elementFormDefault="qualified"
targetNamespace="http://www.backbase.com/ns/widgets"
xmlns:bb="http://www.backbase.com/ns/widgets"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- widget definition root element -->
<xs:element name="widget" type="bb:widgetType"/>
<!-- widget definition data types -->
<xs:complexType name="resourcesType">
<xs:sequence>
<xs:element type="bb:resourceType" name="resource" maxOccurs="unbounded" minOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="widgetType">
<xs:sequence>
<xs:element type="bb:resourcesType" name="resources" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="resourceType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="type" use="required"/>
<xs:attribute type="xs:string" name="src" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
In order to generate and consume these type of messages we create our model WidgetConfig and WidgetConfigRef.

@XmlRootElement(name = "widget", namespace = "http://www.backbase.com/ns/widgets")
@XmlAccessorType(XmlAccessType.FIELD)
public class WidgetConfig {
@XmlElementWrapper(name = "resources", namespace = "http://www.backbase.com/ns/widgets")
@XmlElement(name = "resource", namespace = "http://www.backbase.com/ns/widgets")
private List<WidgetResourceRef> widgetResourceRefs;
public WidgetConfig() {
widgetResourceRefs = new ArrayList<>();
}
public void addWidgetResource(WidgetResourceRef widgetResourceRef) {
widgetResourceRefs.add(widgetResourceRef);
}
public List<WidgetResourceRef> getWidgetResourceRefs() {
return widgetResourceRefs;
}
}
@XmlAccessorType(XmlAccessType.FIELD)
public class WidgetResourceRef {
@XmlAttribute
private String type;
@XmlAttribute
private String src;
/** Default constructor, needed for JAXB framework */
public WidgetResourceRef() {
}
public WidgetResourceRef(String type, String src) {
this.type = type;
this.src = src;
}
public String getType() {
return type;
}
public String getSrc() {
return src;
}
public void setType(String type) {
this.type = type;
}
public void setSrc(String src) {
this.src = src;
}
}
With the namespace attribute of @XmlRootElement, @XmlElementWrapper and @XmlElement we configure the namespace on all XML elements found in the XSD. Note the use of @XmlElementWrapper, which is used to produce a wrapper XML element around resource elements. In order to set the namespace prefix (in this case "bb") we need the following:

@XmlSchema(
xmlns = {
@XmlNs(prefix = "bb", namespaceURI ="http://www.backbase.com/ns/widgets"),
},
elementFormDefault = XmlNsForm.QUALIFIED
)
package com.za.jaxb.widget;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Next we use Spring's excellent Object/XML Mapping support by defining a marshaller/unmarshaller as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/oxm
http://www.springframework.org/schema/oxm/spring-oxm.xsd">
<oxm:jaxb2-marshaller id="marshaller">
<oxm:class-to-be-bound name="com.za.jaxb.widget.WidgetConfig" />
<oxm:class-to-be-bound name="com.za.jaxb.widget.WidgetResourceRef" />
</oxm:jaxb2-marshaller>
</beans>
And we are ready. A working example you can find on my github account. Note, if you are still using Java 6, you need to update the JAXB implementation by including the following dependencies. Otherwise the namespace prefixes will not be set. The JAXB implementation in Java 7 hasn't got this issue.

<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.7</version>
</dependency>