在不使用NamespacePrefixMapper的情况下定义Spring JAXB名称空间

[根据理解进展重编辑]

是否有可能让Spring Jaxb2Marshaller使用一组自定义的命名空间前缀(或者至少尊重模式文件/注释中给出的名称空间前缀),而不必使用NamespacePrefixMapper的扩展名?

我们的想法是让一个类与另一个类具有“has”关系,而另一个类又包含具有不同命名空间的属性。 为了更好地说明这一点,请考虑以下使用JDK1.6.0_12的项目大纲(最新的我可以在工作中得到)。 我在org.example.domain包中有以下内容:

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; } 

ChildElementWithXLink.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; 

运行Main.main()给出以下输出:

     

而我想要的是:

     

一旦这部分工作,那么问题就转移到在Spring中配置Jaxb2Marshaller(Spring 2.5.6,spring-oxm-tiger-1.5.6提供Jaxb2Marshaller),以便通过简单的上下文配置提供相同的function。调用marshal()。

感谢您对此问题的持续关注!

[提供JAXB-RI替代方案的一些编辑在本文末尾]

经过多次努力之后,我终于不得不接受我的环境(Windows XP上的JDK1.6.0_12和Mac Leopard上的JDK1.6.0_20)我不能在不诉诸NamespacePrefixMapper的邪恶的情况下完成这项工作。 为什么这是邪恶的? 因为它强制依赖生产代码中的内部JVM类。 这些类不构成JVM和代码之间可靠接口的一部分(即它们在JVM的更新之间发生变化)。

在我看来,Sun应该解决这个问题,或者有更深入了解的人可以添加这个答案 – 请做!

继续。 因为NamespacePrefixMapper不应该在JVM之外使用,所以它不包含在javac的标准编译路径中(由ct.sym控制的rt.jar的子部分)。 这意味着任何依赖它的代码都可能在IDE中编译良好,但在命令行(即Maven或Ant)会失败。 要解决这个问题,必须在构建中明确包含rt.jar文件,如果路径中有空格,Windows似乎也会遇到麻烦。

如果你发现自己处于这个位置,这里有一个Maven片段可以让你摆脱困境:

  com.sun.xml.bind jaxb-impl 2.1.9 system  C:/temp/rt.jar  

注意垃圾硬编码路径到rt.jar的奇怪地方。 您可以通过{java.home} /lib/rt.jar的组合来解决这个问题,它可以在大多数操作系统上运行,但由于Windows空间问题无法保证。 是的,您可以使用配置文件并相应地激活…

或者,在Ant中,您可以执行以下操作:

    // Add paths for build.classpath and define {src},{target} as usual       

什么是Jaxb2Marshaller Spring配置? 在这里它是完整的,我自己的NamespacePrefixMapper:

弹簧:

     org.example.domain       true      

然后我的NamespacePrefixMapper代码:

 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 ""; } } } 

那就是它。 我希望这可以帮助别人避免我经历的痛苦。 哦,顺便说一句,如果你在Jetty中使用上面的邪恶方法,你可能会遇到以下exception:

java.lang.IllegalAccessError:class sun.reflect.GeneratedConstructorAccessor23无法访问其超类sun.reflect.ConstructorAccessorImpl

祝你好运排序。 线索:您的Web服务器的bootclasspath中的rt.jar。

[显示JAXB-RI(参考实现)方法的额外编辑]

如果您能够将JAXB-RI库引入代码中,则可以进行以下修改以获得相同的效果:

主要:

 // 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; 

从/ lib文件夹中的JAXB-RI下载(跳过许可证箍之后)添加以下JAR:

 jaxb-impl.jar 

运行Main.main()会产生所需的输出。

(重编辑的回复)

我相信你的代码中的问题是由于一些命名空间URI不匹配。 有时您使用http://www.example.org/abc ,有时使用“www.example.org/abc” 。 以下应该做的伎俩:

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); } } 

RootElement.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; } 

ChildElementWithXLink.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; 

现在运行Main.main()给出以下输出:

     

@Blaise:你能用这个信息更新MOXy的文档:

在不使用NamespacePrefixMapper的情况下定义Spring JAXB名称空间

我认为没有在那里描述如何配置名称空间前缀。 谢谢!

JDK 7中的JAXB实现支持名称空间前缀。 我试过JDK 1.6.0_21没有运气。