Java 不使用 NamespacePrefixMapper 定义 Spring JAXB 命名空间
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3289644/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Define Spring JAXB namespaces without using NamespacePrefixMapper
提问by Gary Rowe
[Heavily edited as understanding progresses]
[随着理解的进展进行大量编辑]
Is it possible to get Spring Jaxb2Marshaller to use a custom set of namespace prefixes (or at least respect the ones given in the schema file/annotations) without having to use an extension of a NamespacePrefixMapper?
是否可以让 Spring Jaxb2Marshaller 使用一组自定义的命名空间前缀(或至少尊重架构文件/注释中给出的那些),而不必使用 NamespacePrefixMapper 的扩展?
The idea is to have a class with a "has a" relationship to another class that in turn contains a property with a different namespace. To better illustrate this consider the following project outline which uses JDK1.6.0_12 (the latest I can get my hands on at work). I have the following in the package org.example.domain:
这个想法是让一个类与另一个类具有“具有”关系,而另一个类又包含具有不同命名空间的属性。为了更好地说明这一点,请考虑以下使用 JDK1.6.0_12(我可以在工作中使用的最新版本)的项目大纲。我在包 org.example.domain 中有以下内容:
Main.java:
主.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:
根元素.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:
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:
包-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 output:
运行 Main.main() 给出以下输出:
<?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 what I would like is:
而我想要的是:
<?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>
Once this part is working, then the problem moves on to configuring the Jaxb2Marshaller in Spring (Spring 2.5.6, with spring-oxm-tiger-1.5.6 providing Jaxb2Marshaller) so that it provides the same by means of a simple context configuration and a call to marshal().
一旦这部分工作,那么问题就会转移到在 Spring 中配置 Jaxb2Marshaller(Spring 2.5.6,spring-oxm-tiger-1.5.6 提供 Jaxb2Marshaller),以便它通过简单的上下文配置提供相同的内容和对 marshal() 的调用。
Thank you for your continued interest in this problem!
感谢您对这个问题的持续关注!
采纳答案by Gary Rowe
[Some edits to offer a JAXB-RI alternative are at the end of this post]
[本文末尾提供了一些提供 JAXB-RI 替代方案的编辑]
Well after much head scratching I've finally had to accept that for my environment (JDK1.6.0_12 on Windows XP and JDK1.6.0_20 on Mac Leopard) I just can't make this work without resorting to the evil that is the NamespacePrefixMapper. Why is it evil? Because it forces a reliance on an internal JVM class in your production code. These classes do not form part of a reliable interface between the JVM and your code (i.e. they change between updates of the JVM).
经过多次摸索,我终于不得不接受我的环境(Windows XP 上的 JDK1.6.0_12 和 Mac Leopard 上的 JDK1.6.0_20)我无法在不诉诸 NamespacePrefixMapper 的邪恶的情况下完成这项工作. 为什么是邪恶的?因为它强制依赖您的生产代码中的内部 JVM 类。这些类不构成 JVM 和您的代码之间可靠接口的一部分(即它们在 JVM 更新之间发生变化)。
In my opinion Sun should address this issue or someone with deeper knowledge could add to this answer - please do!
在我看来,Sun 应该解决这个问题,或者有更深入了解的人可以添加到这个答案中 - 请做!
Moving on. Because NamespacePrefixMapper is not supposed to be used outside of the JVM it is not included in the standard compile path of javac (a subsection of rt.jar controlled by ct.sym). This means that any code that depends on it will probably compile fine in an IDE, but will fail at the command line (i.e. Maven or Ant). To overcome this the rt.jar file must be explicitly included in the build, and even then Windows seems to have trouble if the path has spaces in it.
继续。因为 NamespacePrefixMapper 不应该在 JVM 之外使用,所以它不包含在 javac(由 ct.sym 控制的 rt.jar 的一个子部分)的标准编译路径中。这意味着任何依赖于它的代码都可能在 IDE 中编译良好,但在命令行(即 Maven 或 Ant)中会失败。为了克服这个问题,rt.jar 文件必须明确包含在构建中,即使路径中有空格,Windows 似乎也有问题。
If you find yourself in this position, here is a Maven snippet that will get you out of trouble:
如果你发现自己处于这个位置,这里有一个 Maven 代码片段可以让你摆脱困境:
<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>
Note the rubbish hard coded path to a weird place for rt.jar. You could get around this with a combination of {java.home}/lib/rt.jar which will work on most OSs but because of the Windows space issue is not guaranteed. Yes, you can use profiles and activate accordingly...
注意 rt.jar 一个奇怪的地方的垃圾硬编码路径。您可以使用 {java.home}/lib/rt.jar 的组合来解决此问题,该组合适用于大多数操作系统,但由于 Windows 空间问题无法保证。是的,您可以使用配置文件并相应地激活...
Alternatively, in Ant you can do the following:
或者,在 Ant 中,您可以执行以下操作:
<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 Jaxb2Marshaller Spring configuration? Well here it is, complete with my own NamespacePrefixMapper:
Jaxb2Marshaller Spring 配置怎么样?好了,它是我自己的 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's 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:
然后我的 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 "";
}
}
}
Well there it is. I hope this helps someone avoid the pain I went through. Oh, by the way, you may run into the following exception if you use the above evil approach within Jetty:
嗯,就是这样。我希望这可以帮助某人避免我所经历的痛苦。哦,顺便说一句,如果你在 Jetty 中使用上述邪恶的方法,你可能会遇到以下异常:
java.lang.IllegalAccessError: class sun.reflect.GeneratedConstructorAccessor23 cannot access its superclass sun.reflect.ConstructorAccessorImpl
java.lang.IllegalAccessError:类 sun.reflect.GeneratedConstructorAccessor23 无法访问其超类 sun.reflect.ConstructorAccessorImpl
So good luck sorting that one out. Clue: rt.jar in the bootclasspath of your web server.
祝你好运把那个整理出来。提示:您的 Web 服务器的引导类路径中的 rt.jar。
[Extra edits to show the JAXB-RI (Reference Implementation) approach]
[显示 JAXB-RI(参考实现)方法的额外编辑]
If you're able to introduce the JAXB-RI librariesinto your code you can make the following modifications to get the same effect:
如果您能够将JAXB-RI 库引入您的代码中,您可以进行以下修改以获得相同的效果:
Main:
主要的:
// Add a new property that implies external access
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
MyNamespacePrefixMapper:
MyNamespacePrefixMapper:
// Change the import to this
import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
Add the following JAR from JAXB-RI download (after jumping through license hoops) from the /lib folder:
从 /lib 文件夹中的 JAXB-RI 下载添加以下 JAR(在跳过许可证箍之后):
jaxb-impl.jar
Running Main.main() results in the desired output.
运行 Main.main() 会产生所需的输出。
回答by bdoughan
(Heavily edited reponse)
(经过大量编辑的回复)
I believe the problem in your code is due to some namespace URI mismatches. Sometimes you are using "http://www.example.org/abc"and other times "www.example.org/abc". The following should do the trick:
我相信您的代码中的问题是由于某些命名空间 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
根元素.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
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
包信息.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 output:
现在运行 Main.main() 给出以下输出:
<?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>
回答by basZero
@Blaise: Can you update the documentation of MOXy with this info:
@Blaise:您能否使用以下信息更新 MOXy 的文档:
Define Spring JAXB namespaces without using NamespacePrefixMapper
不使用 NamespacePrefixMapper 定义 Spring JAXB 命名空间
I think it is not described there how you can configure the namespace prefixes. Thanks!
我认为那里没有描述如何配置命名空间前缀。谢谢!
回答by Sayantam
JAXB implementation in JDK 7 supports the namespace prefix. I have tried with JDK 1.6.0_21 with no luck.
JDK 7 中的 JAXB 实现支持命名空间前缀。我试过 JDK 1.6.0_21 没有运气。