Thursday, September 24, 2009

Unmarshalling and validating xml without a namespace using JAXB

Unmarshalling and validating xml using jaxb where the xml does not have a namespace specified has driven me crazy for years now. I even did text insertion on the xml before unmarshalling as a workaround. Here's a good way of doing it that's great for xml without any namespace attributes (on the child elements too):

This code unmarshals the object into an org.jdom.Document object then recurses through the object setting namespaces. That object is then passed as a Source to the unmarshaller.

Note also that I did not have @XmlRootElement annotation on GCOM3DRequest so I asked the unmarshaller to unmarshal to that class explicitly.

Primary source for this was here.

package au.gov.amsa.er.nwm.scenario;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.apache.log4j.Logger;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.transform.JDOMSource;
import org.xml.sax.SAXException;

import au.gov.amsa.nwm.gcom3drequest.GCOM3DRequest;
import au.gov.amsa.nwm.gcom3drequest.ObjectFactory;

public class RequestMarshaller {

private static Logger log = Logger.getLogger(RequestMarshaller.class);
private JAXBContext jc;
private Schema schema;

public RequestMarshaller() {
try {
jc = JAXBContext.newInstance(ObjectFactory.class.getPackage()
.getName());
} catch (JAXBException e) {
throw new RuntimeException(e);
}

loadSchema();

}

private void loadSchema() {
SchemaFactory sf = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
schema = sf.newSchema(getClass().getResource(
"request/gcom3dRequest.xsd"));
} catch (SAXException e) {
throw new RuntimeException(e);
}
}

private Unmarshaller createUnmarshaller() {
Unmarshaller u;
try {
u = jc.createUnmarshaller();
// validate against the schema
u.setSchema(schema);
return u;
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}

private static Namespace DEFAULT_NS = Namespace
.getNamespace("http://www.amsa.gov.au/nwm/gcom3dRequest");

public GCOM3DRequest unmarshal(InputStream is) throws JAXBException {

try {
SAXBuilder sb = new SAXBuilder(false);
org.jdom.Document doc = sb.build(is);
setNamespace(doc.getRootElement(), DEFAULT_NS, true);

Source src = new JDOMSource(doc);
Unmarshaller u = createUnmarshaller();
u.setSchema(schema);
JAXBElement element = u.unmarshal(src,
GCOM3DRequest.class);
GCOM3DRequest gcom3d = element.getValue();
return gcom3d;
} catch (IOException e) {
throw new RuntimeException(e);
} catch (JDOMException e) {
throw new RuntimeException(e);
}
}

private static void setNamespace(Element elem, Namespace ns, boolean recurse) {
elem.setNamespace(ns);
if (recurse) {
for (Object o : elem.getChildren()) {
setNamespace((Element) o, ns, recurse);
}
}
}

public Marshaller createMarshaller() {
Marshaller m;
try {
m = jc.createMarshaller();
m.setSchema(schema);
return m;
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}

}

No comments: