4

状況

EclipseLinkのMOXyを使用しており、Mapインターフェイスを実装するクラスで外部OXマッピングXMLを使用しようとしています。ただし、JAXBContextを作成しようとするたびに、次のNPEが取得されます。

Caused by: javax.xml.bind.JAXBException
 - with linked exception:
[java.lang.NullPointerException]
    at org.eclipse.persistence.jaxb.JAXBContext$TypeMappingInfoInput.createContextState(JAXBContext.java:832)
    at org.eclipse.persistence.jaxb.JAXBContext.<init>(JAXBContext.java:143)
    at org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:142)
    at org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:129)
    at org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:93)
    at org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:83)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:210)
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:336)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574)
    at com.example.MOXyOXTest<clinit>(MOXyOXTest.java:59)
Caused by: java.lang.NullPointerException
    at org.eclipse.persistence.jaxb.compiler.XMLProcessor.processXML(XMLProcessor.java:202)
    at org.eclipse.persistence.jaxb.compiler.Generator.<init>(Generator.java:145)
    at org.eclipse.persistence.jaxb.JAXBContext$TypeMappingInfoInput.createContextState(JAXBContext.java:829)

詳細

この問題は、マップされているクラスがjava.util.Mapインターフェースを実装している場合にのみ発生します。マッピングしているクラスがそのインターフェースを実装していない場合、すべてが正常に機能します。これが私がマップしようとしているクラスの単純化された例です:

package com.example;

import java.util.Map;

// This class just wraps a java.util.HashMap
import com.xetus.lib.type.DelegatedMap;

public class SampleClassA extends DelegatedMap<String, Object>{

    public SampleClassA(){
        super();
    }

    public SampleClassA(Map<String, Object> m){
        super(m);
    }

    public void setSomeProperty(String value){
        put("somevalue", value);
    }

    public String getSomeProperty(){
        return (String) get("somevalue");
    }
}

これが私が使用したいMOXyOXメタデータの簡略化されたサンプルです:

<?xml version="1.0"?>
<xml-bindings
  xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
  package-name="com.example"
  xml-mapping-metadata-complete="true"> 
  <java-types>
  <java-type name="SampleClassA" xml-accessor-type="NONE">
   <xml-root-element name="SAMPLE" />
    <java-attributes>
     <xml-attribute type="java.lang.String" name="SomeProperty" required="true">
     <xml-access-methods get-method="getSomeProperty" set-method="setSomeProperty"/>
    </xml-attribute>
   </java-attributes>
  </java-type>
 </java-types>
</xml-bindings>

JAXBContextを作成する方法は次のとおりです

Map<String, Object> props = new HashMap<String, Object>(1);
List bindings = new ArrayList(1);
bindings.add(new StreamSource(MOXyOXTest.class.getResourceAsStream("test-mappings.xml")));
props.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, bindings);

cntxt = JAXBContext.newInstance(new Class[] { SampleClassA.class }, props);

重要な場合に備えて、EclipseLinkバージョン2.3.2を使用しています。バージョン2.2.1でも試してみましたが、同じ結果になりました。

私の質問

java.util.Mapインターフェースを実装するクラスでJAXBを使用しようとしたのはこれが初めてで、基本的なものが欠けているかどうか知りたいです。OXマッピングがマップの名前/値のペアで機能することは期待していませんが、代わりに、クラスに追加されたカスタムのゲッターとセッターで機能します。

このような構成は機能するはずですか?

追加の詳細

  1. サンプルコードで使用されているDelegatedMapは、 java.util.HashMapを拡張せず、インスタンスをラップしてMapインターフェイスを実装するだけです。また、そのクラスには@XmlAccessorType(XmlAccessType.NONE)という注釈が付けられています。
  2. SampleClassAに使用するMapインターフェイスを実装する抽象クラスに関係なく、同じエラーが発生します。SampleClassAがマップを実装しないクラスを拡張する場合、すべてが正しく動作します。
  3. 私が使用しているコードベースでは、Mapインターフェイスを実装するために多くのクラスが必要です。
4

1 に答える 1

2

注: 私はEclipseLink JAXB(MOXy)のリーダーであり、JAXB(JSR-222)エキスパートグループのメンバーです。

これは非常に興味深いユースケースです。JAXB(JSR-222)には、マップとドメインオブジェクトの表現があるため、ハイブリッドオブジェクトがどのように動作するかを検討するのは興味深いことです。サポートを導入するために、次の拡張リクエストを追加しました。


アップデート

この拡張機能の実装が完了しました。次の場所から、2012年4月19日に開始されるEclipseLink2.4.0の夜間ダウンロードを使用して試すことができます。

この修正には、super-typeプロパティを利用してスーパータイプを指定し、実際のスーパータイプをオーバーライドすることが含まれます。このsuper-typeプロパティは、以前は動的JAXBサポートによってのみ利用されていました。

bindings.xml

<?xml version="1.0"?>
<xml-bindings 
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum10075634">
    <java-types>
        <java-type name="SampleClassA" super-type="java.lang.Object" xml-accessor-type="NONE">
            <xml-root-element name="SAMPLE" />
            <java-attributes>
                <xml-attribute java-attribute="someProperty" name="SomeProperty" required="true"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

DelegatedMap

DelegatatedMap以下は、あなたの質問で説明されているクラスの実装です。

package forum10075634;

import java.util.*;

public class DelegatedMap<K,V> implements Map<K,V> {

    private Map<K,V> map;

    public DelegatedMap() {
        map = new HashMap<K,V>();
    }

    public DelegatedMap(Map<K,V> map) {
        this.map = map;
    }

    public void clear() {
        map.clear();
    }

    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }

    public Set<java.util.Map.Entry<K, V>> entrySet() {
        return map.entrySet();
    }

    public V get(Object key) {
        return map.get(key);
    }

    public boolean isEmpty() {
        return map.isEmpty();
    }

    public Set<K> keySet() {
        return map.keySet();
    }

    public V put(K key, V value) {
        return map.put(key, value);
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        map.putAll(m);
    }

    public V remove(Object key) {
        return map.remove(key);
    }

    public int size() {
        return map.size();
    }

    public Collection<V> values() {
        return map.values();
    }

}

SampleClassA

package forum10075634;

import java.util.Map;

public class SampleClassA extends DelegatedMap<String, Object> {

    public SampleClassA() {
        super();
    }

    public SampleClassA(Map<String, Object> m) {
        super(m);
    }

    public void setSomeProperty(String value) {
        put("somevalue", value);
    }

    public String getSomeProperty() {
        return (String) get("somevalue");
    }

}

jaxb.properties

MOXyをJAXBプロバイダーとして指定するにはjaxb.properties、ドメインクラスと同じパッケージで呼び出されるファイルを次のエントリで追加する必要があります。

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

デモ

package forum10075634;

import java.io.StringReader;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>(1);
        properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum10075634/bindings.xml");
        JAXBContext jc = JAXBContext.newInstance(new Class[] {SampleClassA.class}, properties);

        StringReader xml = new StringReader("<SAMPLE SomeProperty='Foo'/>");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        SampleClassA sampleClassA = (SampleClassA) unmarshaller.unmarshal(xml);

        System.out.println(sampleClassA.getSomeProperty());
        System.out.println(sampleClassA.get("somevalue"));

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(sampleClassA, System.out);
    }

}

出力

Foo
Foo
<?xml version="1.0" encoding="UTF-8"?>
<SAMPLE SomeProperty="Foo"/>
于 2012-04-10T15:20:45.413 に答える