一尘不染

不使用NamespacePrefixMapper定义Spring JAXB名称空间

spring

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

想法是让一个类与另一个类具有“具有”关系,而该类又包含具有不同名称空间的属性。为了更好地说明这一点,请考虑以下使用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()给出以下输出:

<?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()。

感谢你一直以来对这个问题的关注!


阅读 791

收藏
2020-04-18

共1个答案

一尘不染

在花了很多力气之后,我终于不得不接受针对我的环境(在Windows XP上为JDK1.6.0_12,在Mac Leopard上为JDK1.6.0_20),我只是在不诉诸于NamespacePrefixMapper这个邪恶的前提下才能完成这项工作。为什么它是邪恶的?因为它强制你的生产代码中依赖内部JVM类。这些类不构成JVM和你的代码之间可靠接口的一部分(即,它们在JVM更新之间进行更改)。

我认为Sun应该解决此问题,否则,如果你的知识更深入,则可以添加此答案-请这样做!

继续。因为不应在JVM外部使用NamespacePrefixMapper,所以它不包含在javac的标准编译路径(由ct.sym控制的rt.jar的子部分)中。这意味着任何依赖于它的代码都可以在IDE中正常编译,但是在命令行(例如Maven或Ant)失败。为了克服这个问题,必须在构建中显式包含rt.jar文件,即使在路径中包含空格的情况下,Windows似乎也会遇到麻烦。

如果你处于这个位置,则可以使用以下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>

注意rt.jar的一个奇怪的地方的垃圾硬编码路径。你可以使用{java.home} /lib/rt.jar的组合来解决此问题,该组合在大多数操作系统上都可以使用,但是由于Windows空间问题,不能保证。是的,你可以使用配置文件并进行相应的激活…

或者,在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>   

以及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"/>

然后是我的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中使用上述邪恶方法,则可能会遇到以下异常:

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

真是太幸运了,整理出一个。提示:Web服务器的引导类路径中的rt.jar。

如果你能够在代码中引入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()会产生所需的输出。

2020-04-18