Question

For example, I've got a simple schema which imports another schema. The second schema (urn:just:attributes, just-attributes.xsd) just defines an attribute group.

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.example.org/MySchema"
    xmlns:tns="http://www.example.org/MySchema" 
    elementFormDefault="qualified"
    xmlns:ja="urn:just:attributes">

    <import schemaLocation="just-attributes.xsd" namespace="urn:just:attributes"/>

    <element name="MyElement">
        <complexType>
            <attributeGroup ref="ja:AttributeGroup"/>
        </complexType>
    </element>
</schema>

I'm using the Metro xjc Ant task to generate classes off of this schema. The problem I'm running into is that the third party application I'm interacting with is peculiar about namespaces. This case I need a String value, so I have to serialize it. I use boilerplate code for this.

private static <T> String marshal(T object) throws JAXBException{
    OutputStream outputStream = new ByteArrayOutputStream();
    JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass());
    Marshaller marshaller = jaxbContext.createMarshaller();
    marshaller.marshal(object, outputStream);
    return outputStream.toString();
}

Which gives me something along the lines of

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:MyElement xmlns:ns1="urn:just:attributes" xmlns:ns2="http://www.example.org/MySchema" ns1:attrib1="1234" ns1:attrib2="5678"/>

The problem I have is that this third party expects something like xmlns:thirdpartyns="urn:just:attributes", which is to say, they are parsing based on the name given to the namespace. It has to be "thirdpartyns" for their software to work.

Does anyone know of a way around this, short of doing a find and replace in the resulting string? A custom binding rule perhaps?

Was it helpful?

Solution

http://hwellmann.blogspot.com/2011/03/jaxb-marshalling-with-custom-namespace.html

This shows how to do it.

Another: http://www.systemmobile.com/?p=280

Key bits in case that link dies too:

the NamespacePrefixMapper class, found in the com.sun.xml.bind.marshaller package. The abstract class has one method to implement:

public abstract String getPreferredPrefix(  
     String namespaceUri,         
     String suggestion,         
     boolean requirePrefix); 

then

Marshaller marshaller =        
    jaxbContext.createMarshaller();        
marshaller.setProperty(”com.sun.xml.bind.namespacePrefixMapper”,        
    new MyNamespacePrefixMapper());  

If you’re also using javax.xml.xpath.XPath, your NamespacePrefixMapper can also implement javax.xml.namespace.NamespaceContext, centralizing your namespace customization in a single class.

OTHER TIPS

I tested that in Java SE6 and it requires a small change compared to the solution for Java SE 5 (as described above):

    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
    m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
    m.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", mapper);

So the third property from above contains the additional .internal. in the package name compared to the Java SE5 version. What I did not find out yet is how to tell the Marshaller which namespace URI becomes the default namespace (""). If I override the method getPreferredPrefix() and return an empty string, the Marshaller has issues with writing attributes of the default namespace (in this case it creates a new namespace called ns1)

I had the same question. In package-info.java (if you don't have it, you can just manually create it) add the xmlns part:

@javax.xml.bind.annotation.XmlSchema(xmlns = {
        @javax.xml.bind.annotation.XmlNs(namespaceURI = "urn:just:attributes", prefix = "thirdpartyns") }, 
        elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)

There is a way of doing this, which uses an internal JAXB implementation class called NamespacePrefixMapper. In the JAXB RI, this is in com.sun.xml.bind.marshaller, but in Java6, it's in com.sun.xml.internal.bind.marshaller.

This is an abstract class, which you can subclass and implement the abstract method which maps namespace URIs on to prefixes.

You then inject an instance of that subclass into the marshaller:

JAXBContext context = ...
Marshaller marshaller = context.createMarshaller();
NamespacePrefixMapper prefixMapper = new MyPrefixMapperImpl();
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", prefixMapper);

The property name is going to be different for the Java6 version, but you get the idea.

Note that this is an internal JAXB implementation class, so there's no guarantee it'll be there in future versions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top