I'm working with JAXB serialization in a MULE project, and need to make the serialized XML as consistent as possible with that produced by existing .NET components.

I had two issues:

  1. JAXB was producing an XML declaration that looked like
    <?xml version="1.0" encoding="UTF8" standalone="yes"?>
    while the .NET-serialized XML had a declaration that looked like
    <?xml version="1.0"?>
  2. There was one repeating element in the XML that was was described as an xsd:anyType and the JAXB-generated XML put the namespace declarations for the 'xsi' and 'xsd' prefixes on each element when it included the "xsd:type" attributes, whereas the .NET-generated XML had these declarations once on the root element.

Neither of these differences should have gotten in the way of interoperability, but I wanted to see if I could make the JAXB-generated XML closer to the .NET-generated XML.

It turns out that JAXB cannot be configured to change its XML declaration, but it can be configured to suppress it entirely, by setting the JAXB marshaler’s JAXB_FRAGMENT property to true. You can then write the XML declaration yourself, like this:

:::java
final ByteArrayOutputStream buff = new ByteArrayOutputStream();
final PrintWriter writer = new PrintWriter(buff);
final Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
writer.println("<?xml version=\"1.0\"?>");
marshaller.marshal(response, writer);

The namespace issue can be resolved by the implementation of a NamespacePrefixMapper subclass. Normally, you'd have to override two methods, getPreDeclaredNamespaceUris to instruct JAXB to declare the namespaces at the root element, and getPreferredPrefix to specify the prefixes for the namespaces.

However, as described by Martin Grebac, the JAXB implementation lead, there is a somewhat-undocumented method, getPreDeclaredNamespaceUris2, that allows you to specify the URIs to be pre-declared, and their prefixes all at the same time. Here’s my example, which includes the custom XML declaration described above:

:::java
private String marshallResponse(final FactivaCSPmessage response) throws Exception {
    final ByteArrayOutputStream buff = new ByteArrayOutputStream();
    final PrintWriter writer = new PrintWriter(buff);
    final Marshaller marshaller = jaxbContext.createMarshaller();
    marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", 
		namespacePrefixMapper);
    marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
    writer.println("<?xml version=\"1.0\"?>");

    marshaller.marshal(response, writer);
    return buff.toString();
}

/**
 * Coerces the JAXB marshaller to declare the "xsi" and "xsd" namespaces at the root element
 * instead of putting them inline on each element that uses one of the namespaces.
 */
private static class CustomNamespacePrefixMapper extends NamespacePrefixMapper {

    @Override
    public String getPreferredPrefix(String namespaceUri, String suggestion, 
					boolean requirePrefix) {
        return suggestion;
    }

    @Override
    public String[] getPreDeclaredNamespaceUris2() {
        return new String[]{
                "xsi",
                "http://www.w3.org/2001/XMLSchema-instance",
                "xsd",
                "http://www.w3.org/2001/XMLSchema"
        };

    }
}