代码之家  ›  专栏  ›  技术社区  ›  Gary

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

  •  25
  • Gary  · 技术社区  · 14 年前

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

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

    根元素.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;
    

    <?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()的调用提供相同的配置。

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

    4 回复  |  直到 14 年前
        1
  •  13
  •   Gary    14 年前

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

    在绞尽脑汁之后,我终于不得不承认,对于我的环境(windowsxp上的JDK1.6.0\u12和macleopard上的JDK1.6.0\u20),如果不借助于NamespacePrefixMapper这个邪恶的东西,我就无法实现这一点。为什么它是邪恶的?因为它强制依赖于生产代码中的内部JVM类。这些类不构成JVM和代码之间可靠接口的一部分(即,它们在JVM的更新之间发生变化)。

    如果你发现自己处于这种境地,这里有一个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>    
    

    那么jaxb2的marshaller-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"/>
    

    然后我的命名空间前缀映射代码:

    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

    所以祝你好运。提示:rt.jar位于web服务器的bootclasspath中。

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

    如果你能介绍 JAXB-RI libraries 在代码中,可以进行以下修改以获得相同的效果:

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

    jaxb-impl.jar
    

    运行Main.Main()会得到所需的输出。

        2
  •  9
  •   bdoughan    14 年前

    (大量编辑回复)

    http://www.example.org/abc " "www.example.org/abc" . 下面应该可以做到这一点:

    主类

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

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

    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; 
    

    <?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>
    
        3
  •  1
  •   basZero    8 年前

    @布莱斯:你能用以下信息更新莫西的文档吗

    Define Spring JAXB namespaces without using NamespacePrefixMapper

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

        4
  •  0
  •   Sayantam    13 年前

    jdk7中的JAXB实现支持名称空间前缀。我试过JDK1.6.0Ø21,但运气不好。