web-dev-qa-db-ja.com

NamespacePrefixMapperを使用せずにSpring JAXB名前空間を定義する

[理解が進むにつれて大幅に編集]

NamespacePrefixMapperの拡張機能を使用せずに、Spring Jaxb2Marshallerにカスタムの名前空間プレフィックスのセットを使用する(または少なくともスキーマファイル/アノテーションで指定されたプレフィックスを尊重する)ことは可能ですか?

アイデアは、別の名前空間を持つプロパティを含む別のクラスとの「has a」関係を持つクラスを持つことです。これをよりわかりやすく説明するために、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()の呼び出し。

この問題に引き続き関心をお寄せいただきありがとうございます。

25
Gary Rowe

[JAXB-RIの代替案を提供するためのいくつかの編集は、この投稿の最後にあります]

よく頭を引っかいた後、私はようやく自分の環境でそれを受け入れる必要がありました(WindowsのJDK1.6.0_12 XPおよびMac LeopardのJDK1.6.0_20)。私はこの作業を行うことができません。 NamespacePrefixMapperである悪に頼る必要はありません。なぜ悪なのでしょうか?これは運用コードの内部JVMクラスへの依存を強制するためです。これらのクラスは、JVMとコードの間の信頼できるインターフェースの一部を形成しません(つまり、それらは変更されます) JVMの更新間)。

私の意見では、Sunはこの問題に対処する必要があります。

次へ。 NamespacePrefixMapperはJVMの外部での使用は想定されていないため、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の奇妙な場所へのごみのハードコードされたパスに注意してください。ほとんどのOSで動作する{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を完成させます:

春:

<!-- 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サーバーのbootclasspathにあるrt.jar。

[JAXB-RI(参照実装)アプローチを示すための追加編集]

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()を実行すると、目的の出力が得られます。

12
Gary Rowe

(大幅に編集された応答)

あなたのコードの問題は、いくつかの名前空間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

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

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; 

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>
9
bdoughan

@ブレイズ:この情報でMOXyのドキュメントを更新できますか?

NamespacePrefixMapperを使用せずにSpring JAXB名前空間を定義する

名前空間の接頭辞をどのように構成できるかは、ここでは説明されていません。ありがとう!

1
basZero

JDK 7のJAXB実装は、名前空間接頭辞をサポートしています。私は運が悪いのにJDK 1.6.0_21で試しました。

0
Sayantam