[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> <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:
<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="contextPaths"> <list> <value>org.example.domain</value> </list> </property> <property name="marshallerProperties"> <map> <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> <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.