Define Spring JAXB namespaces without using NamespacePrefixMapper

[Strongly edited as an understanding progress]

Is it possible to use Spring Jaxb2Marshaller to use a custom set of namespace prefixes (or at least abide by those specified in the schema file / annotation) without using the NamespacePrefixMapper extension?

The idea is to have a class with a β€œhas” relationship to another class, which in turn contains a property with a different namespace. To better illustrate this, consider the following project, which uses JDK1.6.0_12 (the last thing I can get at work). I have the following in the org.example.domain package:

Main.java:

package org.example.domain; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; public class Main { public static void main(String[] args) throws JAXBException { JAXBContext jc = JAXBContext.newInstance(RootElement.class); RootElement re = new RootElement(); re.childElementWithXlink = new ChildElementWithXlink(); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(re, System.out); } } 

RootElement.java:

 package org.example.domain; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(namespace = "www.example.org/abc", name="Root_Element") public class RootElement { @XmlElement(namespace = "www.example.org/abc") public ChildElementWithXlink childElementWithXlink; } 

ChildElementWi Thank Link.java:

 package org.example.domain; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSchemaType; @XmlRootElement(namespace="www.example.org/abc", name="Child_Element_With_XLink") public class ChildElementWithXlink { @XmlAttribute(namespace = "http://www.w3.org/1999/xlink") @XmlSchemaType(namespace = "http://www.w3.org/1999/xlink", name = "anyURI") private String href="http://www.example.org"; } 

package-info.java:

 @javax.xml.bind.annotation.XmlSchema( namespace = "http://www.example.org/abc", xmlns = { @javax.xml.bind.annotation.XmlNs(prefix = "abc", namespaceURI ="http://www.example.org/abc"), @javax.xml.bind.annotation.XmlNs(prefix = "xlink", namespaceURI = "http://www.w3.org/1999/xlink") }, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package org.example.domain; 

Running Main.main () gives the following result:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ns2:Root_Element xmlns:ns1="http://www.w3.org/1999/xlink" xmlns:ns2="www.example.org/abc"> <ns2:childElementWithXlink ns1:href="http://www.example.org"/> </ns2:Root_Element> 

whereas I would like to:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <abc:Root_Element xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:abc="www.example.org/abc"> <abc:childElementWithXlink xlink:href="http://www.example.org"/> </abc:Root_Element> 

After this part works, the problem goes on to configure Jaxb2Marshaller in Spring (Spring 2.5.6, with spring -oxm-tiger-1.5.6 providing Jaxb2Marshaller) so that it provides the same with a simple context configuration and marshal call ().

Thank you for your continued interest in this issue!

+23
java spring xml jaxb
Jul 20 2018-10-11T00:
source share
4 answers

[Some changes suggesting an alternative to JAXB-RI are at the end of this post]

After multiple scratches, I finally had to agree that for my environment (JDK1.6.0_12 on Windows XP and JDK1.6.0_20 on Mac Leopard) I just can't do this work without resorting to the evil that NamespacePrefixMapper . Why is this evil? Because it makes you rely on the internal JVM class in your production code. These classes are not part of the robust interface between the JVM and your code (i.e., they change between JVM updates).

In my opinion, Sun should solve this problem, or someone with deeper knowledge can add to this answer - please do it!

Moving. Since NamespacePrefixMapper is not intended to be used outside the JVM, it is not included in the standard javac compilation path (the rt.jar subsection controlled by ct.sym). This means that any code that depends on it will probably compile in the IDE, but will not work on the command line (i.e. Maven or Ant). To overcome this, the rt.jar file must be explicitly included in the assembly, and even then Windows seems to have problems if there are spaces in the path.

If you find yourself in this position, here is a fragment of Maven that will save you from trouble:

 <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.1.9</version> <scope>system</scope> <!-- Windows will not find rt.jar if it is in a path with spaces --> <systemPath>C:/temp/rt.jar</systemPath> </dependency> 

Pay attention to the hard encoding of garbage in a strange place for rt.jar. You can get around this with a combination of {java.home} /lib/rt.jar, which will work on most operating systems, but is not guaranteed due to problems with Windows space. Yes, you can use profiles and activate accordingly ...

Alternatively, in Ant, you can do the following:

 <path id="jre.classpath"> <pathelement location="${java.home}\lib" /> </path> // Add paths for build.classpath and define {src},{target} as usual <target name="compile" depends="copy-resources"> <mkdir dir="${target}/classes"/> <javac bootclasspathref="jre.classpath" includejavaruntime="yes" debug="on" srcdir="${src}" destdir="${target}/classes" includes="**/*"> <classpath refid="build.classpath"/> </javac> </target> 

And what of the configuration of Jaxb2Marshaller Spring? Well, that’s it, with my own NamespacePrefixMapper:

Spring:

 <!-- JAXB2 marshalling (domain objects annotated with JAXB2 meta data) --> <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="contextPaths"> <list> <value>org.example.domain</value> </list> </property> <property name="marshallerProperties"> <map> <!-- Good for JDK1.6.0_6+, lose 'internal' for earlier releases - see why it evil? --> <entry key="com.sun.xml.internal.bind.namespacePrefixMapper" value-ref="myCapabilitiesNamespacePrefixMapper"/> <entry key="jaxb.formatted.output"><value type="boolean">true</value></entry> </map> </property> </bean> <!-- Namespace mapping prefix (ns1->abc, ns2->xlink etc) --> <bean id="myNamespacePrefixMapper" class="org.example.MyNamespacePrefixMapper"/> 

Then my NamespacePrefixMapper code:

 public class MyNamespacePrefixMapper extends NamespacePrefixMapper { public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { if (requirePrefix) { if ("http://www.example.org/abc".equals(namespaceUri)) { return "abc"; } if ("http://www.w3.org/1999/xlink".equals(namespaceUri)) { return "xlink"; } return suggestion; } else { return ""; } } } 

Well it is. Hope this helps someone avoid the pain I experienced. By the way, you may encounter the following exception if you use the aforementioned evil approach in Jetty:

java.lang.IllegalAccessError: class sun.reflect.GeneratedConstructorAccessor23 cannot access its superclass sun.reflect.ConstructorAccessorImpl

So good luck sorting this out. Hint: rt.jar in the bootclasspath of your web server.

[Additional changes to show the JAXB-RI (Reference Implementation) approach]

If you can inject JAXB-RI libraries into your code, you can make the following changes to get the same effect:

Main:

 // Add a new property that implies external access marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper()); 

MyNamespacePrefixMapper:

 // Change the import to this import com.sun.xml.bind.marshaller.NamespacePrefixMapper; 

Add the following JAR from the JAXB-RI boot (after jumping the license hoops) from the / lib folder:

 jaxb-impl.jar 

Running Main.main () produces the desired result.

+12
Jul 22 '10 at 8:37
source share

(Strongly edited answer)

I believe the problem in your code is due to mismatch of some namespace URIs. Sometimes you use " http://www.example.org/abc " and other times "www.example.org/abc" . The following should do the trick:

Main.java

 package org.example.domain; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; public class Main { public static void main(String[] args) throws JAXBException { JAXBContext jc = JAXBContext.newInstance(RootElement.class); System.out.println(jc); RootElement re = new RootElement(); re.childElementWithXlink = new ChildElementWithXlink(); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(re, System.out); } } 

Rootoot.java

 package org.example.domain; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(namespace="http://www.example.org/abc", name="Root_Element") public class RootElement { @XmlElement(namespace = "http://www.example.org/abc") public ChildElementWithXlink childElementWithXlink; } 

ChildElementWi Thank Link.java

 package org.example.domain; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSchemaType; @XmlRootElement(namespace="http://www.example.org/abc", name="Child_Element_With_XLink") public class ChildElementWithXlink { @XmlAttribute(namespace = "http://www.w3.org/1999/xlink") @XmlSchemaType(namespace = "http://www.w3.org/1999/xlink", name = "anyURI") private String href="http://www.example.org"; } 

package-info.java

 @javax.xml.bind.annotation.XmlSchema( namespace = "http://www.example.org/abc", xmlns = { @javax.xml.bind.annotation.XmlNs(prefix = "abc", namespaceURI ="http://www.example.org/abc"), @javax.xml.bind.annotation.XmlNs(prefix = "xlink", namespaceURI = "http://www.w3.org/1999/xlink") }, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package org.example.domain; 

Now running Main.main () gives the following result:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <abc:Root_Element xmlns:abc="http://www.example.org/abc" xmlns:xlink="http://www.w3.org/1999/xlink"> <abc:childElementWithXlink xlink:href="http://www.example.org"/> </abc:Root_Element> 
+9
Jul 20 '10 at 15:55
source share

@Blaise: Can you update the MOXy documentation with this information:

Define Spring JAXB namespaces without using NamespacePrefixMapper

I think it is not described here how you can configure namespace prefixes. Thank!

+1
Jan 12 2018-11-12T00:
source share

The JAXB implementation in JDK 7 supports the namespace prefix. I tried with JDK 1.6.0_21 with no luck.

0
Aug 05 2018-11-18T00:
source share