在不使用NamespacePrefixMapper的情况下定义Spring JAXB名称空间 [英] Define Spring JAXB namespaces without using NamespacePrefixMapper

查看:161
本文介绍了在不使用NamespacePrefixMapper的情况下定义Spring JAXB名称空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[通过理解进展进行了大量编辑]

[Heavily edited as understanding progresses]

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

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?

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

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:

Main.java:

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:

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:

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:

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()给出以下输出:

Running Main.main() gives the following output:

<?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>

而我想要的是:

<?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>

一旦这部分工作,问题就转移到在Spring中配置Jaxb2Marshaller(Spring 2.5。 6,使用spring-oxm-tiger-1.5.6提供Jaxb2Marshaller),以便通过简单的上下文配置和对marshal()的调用来提供相同的功能。

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().

感谢您对此问题的持续兴趣!

Thank you for your continued interest in this problem!

推荐答案

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

[Some edits to offer a JAXB-RI alternative are at the end of this post]

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

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).

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

In my opinion Sun should address this issue or someone with deeper knowledge could add to this answer - please do!

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

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.

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

If you find yourself in this position, here is a Maven snippet that will get you out of 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>

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

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

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

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>    

什么是Jaxb2Marshaller Spring配置?好吧,完成我自己的NamespacePrefixMapper:

And what of the Jaxb2Marshaller Spring configuration? Well here it is, complete 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'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"/>

然后我的NamespacePrefixMapper代码:

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

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

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:

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

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

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

So good luck sorting that one out. Clue: rt.jar in the bootclasspath of your web server.

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

[Extra edits to show the JAXB-RI (Reference Implementation) approach]

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

If you're able to introduce the JAXB-RI libraries into your code you can make the following modifications to get the same effect:

主要:

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

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

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

jaxb-impl.jar

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

Running Main.main() results in the desired output.

这篇关于在不使用NamespacePrefixMapper的情况下定义Spring JAXB名称空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆