不使用 NamespacePrefixMapper 定义 Spring JAXB 命名空间 [英] Define Spring JAXB namespaces without using NamespacePrefixMapper

查看:26
本文介绍了不使用 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:

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:

春天:

<!-- 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 服务器的引导类路径中的 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天全站免登陆