Is it possible to customize the namespace prefix that JAXB uses when marshalling to a String?
Asked Answered
N

4

30

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?

Nutritionist answered 30/12, 2009 at 22:58 Comment(0)
D
28

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.

Drogheda answered 30/12, 2009 at 23:55 Comment(5)
The link is dead... @Drogheda please check it!Strawberry
Thanks, that first link totally helped me.Expect
This only works for Java SE5 but not for SE6, for SE6 please see my comment: https://mcmap.net/q/474701/-is-it-possible-to-customize-the-namespace-prefix-that-jaxb-uses-when-marshalling-to-a-stringStrawberry
Solution 2 of the first link is pretty simple and works like a charm. Thx!Spooner
Since JAXB 3 the correct property is org.glassfish.jaxb.namespacePrefixMapper.Lusty
S
12

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)

Strawberry answered 11/1, 2011 at 13:25 Comment(3)
Apparently in java SE 7, it uses again ”com.sun.xml.bind.namespacePrefixMapper”.... (with the Java SE 6 property I do get an exception)Framework
Does anybody know how it would be correct for Java SE 8? @FrameworkStrawberry
I tried using the mentioned approach but I get the error property "org.glassfish.jaxb.namespacePrefixMapper" is not supported, I have posted the question, can you please once check and provide some response: https://mcmap.net/q/500290/-how-to-use-the-custom-namespaces-from-the-jaxb-ri-error-quot-org-glassfish-jaxb-namespaceprefixmapper-quot-is-not-supported/7584240Schroeder
I
11

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)
Interchange answered 26/10, 2015 at 10:41 Comment(2)
What if I generate sources from xsd? Using apache plugin maven. How can I customize it this way?Fieldpiece
I tried using the mentioned approach but I get the error property "org.glassfish.jaxb.namespacePrefixMapper" is not supported, I have posted the question, can you please once check and provide some response: https://mcmap.net/q/500290/-how-to-use-the-custom-namespaces-from-the-jaxb-ri-error-quot-org-glassfish-jaxb-namespaceprefixmapper-quot-is-not-supported/7584240Schroeder
C
0

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.

Copp answered 31/12, 2009 at 0:18 Comment(2)
Please don't subclass the internal class.Epicure
I tried using the mentioned approach but I get the error property "org.glassfish.jaxb.namespacePrefixMapper" is not supported, I have posted the question, can you please once check and provide some response: https://mcmap.net/q/500290/-how-to-use-the-custom-namespaces-from-the-jaxb-ri-error-quot-org-glassfish-jaxb-namespaceprefixmapper-quot-is-not-supported/7584240Schroeder

© 2022 - 2024 — McMap. All rights reserved.