32

私はいくつかの複雑なデータ構造を持っています

Map< A, Set< B > >
Set< Map< A, B > >
Set< Map< A, Set< B > > >
Map< A, Map< B, Set< C > > >
and so on (more complex data structures)

注:私の場合、SetとListのどちらを使用してもかまいません。

これで、JAXBでXmlAdapterを定義できることがわかりました。これで問題ありませんが、指定されたすべてのデータ構造に対してXmlAdapterを定義したくありません(コピーアンドペーストコードが多すぎます)。

私は2つの一般化XmlAdaptersを宣言することによって私の目標を達成しようとしました:

  • マップ用に1つ:MapAdapter<K,V>
  • セット用に1つ:SetAdapter<V>

問題
JAXBは次のように文句を言います:

javax.xml.bind.JAXBException:
class java.util.Collections$UnmodifiableMap nor any of its
  super class is known to this context.

これが私のアダプタクラスです:

import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

public class Adapters {

public final static class MapAdapter<K, V>
        extends XmlAdapter<MapAdapter.Adapter<K, V>, Map<K, V>> {

    @XmlType
    @XmlRootElement
    public final static class Adapter<K, V> {

        @XmlElement
        protected List<MyEntry<K, V>> key = new LinkedList<MyEntry<K, V>>();

        private Adapter() {
        }

        public Adapter(Map<K, V> original) {
            for (Map.Entry<K, V> entry : original.entrySet()) {
                key.add(new MyEntry<K, V>(entry));
            }
        }

    }

    @XmlType
    @XmlRootElement
    public final static class MyEntry<K, V> {

        @XmlElement
        protected K key;

        @XmlElement
        protected V value;

        private MyEntry() {
        }

        public MyEntry(Map.Entry<K, V> original) {
            key = original.getKey();
            value = original.getValue();
        }

    }

    @Override
    public Adapter<K, V> marshal(Map<K, V> obj) {
        return new Adapter<K, V>(obj);
    }

    @Override
    public Map<K, V> unmarshal(Adapter<K, V> obj) {
        throw new UnsupportedOperationException("unmarshalling is never performed");
    }

}

}

これが私のJUnitテストケースです:

import java.io.*;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;
import org.junit.*;
import static java.lang.System.*;

public class SomeTest {

@Test
public void _map2()
        throws Exception {

    Map<String, Map<String, String>> dataStructure =
            new HashMap<String, Map<String, String>>();

    Map<String, String> inner1 = new HashMap<String, String>();
    Map<String, String> inner2 = new HashMap<String, String>();

    dataStructure.put("a", inner1);
    dataStructure.put("b", inner1);

    inner1.put("a1", "1");
    inner1.put("a2", "2");
    inner2.put("b1", "1");
    inner2.put("b2", "2");

    JAXBContext context = JAXBContext.newInstance(Adapters.XMap.class,
            Adapters.XCount.class, Adapters.XEntry.class);

    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

    marshaller.setAdapter(new Adapters.MapAdapter());

    StringWriter sw = new StringWriter();

    marshaller.marshal(dataStructure, sw);
    out.println(sw.toString());
}

}
4

6 に答える 6

21

XmlAdapter なしで問題を解決しました。

MapMap.Entry、およびCollectionの JAXB アノテーション付きオブジェクトを作成しました。
主なアイデアは、メソッドxmlizeNestedStructure(...)内にあります。

コードを見てください:

public final class Adapters {

private Adapters() {
}

public static Class<?>[] getXmlClasses() {
    return new Class<?>[]{
                XMap.class, XEntry.class, XCollection.class, XCount.class
            };
}

public static Object xmlizeNestedStructure(Object input) {
    if (input instanceof Map<?, ?>) {
        return xmlizeNestedMap((Map<?, ?>) input);
    }
    if (input instanceof Collection<?>) {
        return xmlizeNestedCollection((Collection<?>) input);
    }

    return input; // non-special object, return as is
}

public static XMap<?, ?> xmlizeNestedMap(Map<?, ?> input) {
    XMap<Object, Object> ret = new XMap<Object, Object>();

    for (Map.Entry<?, ?> e : input.entrySet()) {
        ret.add(xmlizeNestedStructure(e.getKey()),
                xmlizeNestedStructure(e.getValue()));
    }

    return ret;
}

public static XCollection<?> xmlizeNestedCollection(Collection<?> input) {
    XCollection<Object> ret = new XCollection<Object>();

    for (Object entry : input) {
        ret.add(xmlizeNestedStructure(entry));
    }

    return ret;
}

@XmlType
@XmlRootElement
public final static class XMap<K, V> {

    @XmlElementWrapper(name = "map")
    @XmlElement(name = "entry")
    private List<XEntry<K, V>> list = new LinkedList<XEntry<K, V>>();

    public XMap() {
    }

    public void add(K key, V value) {
        list.add(new XEntry<K, V>(key, value));
    }

}

@XmlType
@XmlRootElement
public final static class XEntry<K, V> {

    @XmlElement
    private K key;

    @XmlElement
    private V value;

    private XEntry() {
    }

    public XEntry(K key, V value) {
        this.key = key;
        this.value = value;
    }

}

@XmlType
@XmlRootElement
public final static class XCollection<V> {

    @XmlElementWrapper(name = "list")
    @XmlElement(name = "entry")
    private List<V> list = new LinkedList<V>();

    public XCollection() {
    }

    public void add(V obj) {
        list.add(obj);
    }

}

}

できます!

デモ出力を見てみましょう:

<xMap>
    <map>
        <entry>
            <key xsi:type="xCount" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <count>1</count>
                <content xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">a</content>
            </key>
            <value xsi:type="xCollection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <list>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">a1</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">a2</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">a3</entry>
                </list>
            </value>
        </entry>
        <entry>
            <key xsi:type="xCount" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <count>2</count>
                <content xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">b</content>
            </key>
            <value xsi:type="xCollection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <list>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">b1</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">b3</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">b2</entry>
                </list>
            </value>
        </entry>
        <entry>
            <key xsi:type="xCount" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <count>3</count>
                <content xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">c</content>
            </key>
            <value xsi:type="xCollection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <list>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">c1</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">c2</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">c3</entry>
                </list>
            </value>
        </entry>
    </map>
</xMap>

申し訳ありませんが、デモの出力 では、アダプタのソース コードには記載されていない「count」というデータ構造も使用されています。

ところで:これらの迷惑で(私の場合)不要なxsi:type属性をすべて削除する方法を知っている人はいますか?

于 2009-05-04T00:19:21.757 に答える
5

Map< String,Map< String,Integer>> を使用するという同じ要件がありました。私は XMLAdapter を使用しましたが、うまくいきました。XMLAdaptor を使用することは、私が考える最もクリーンなソリューションです。以下はアダプターのコードです。これは jaXb クラスのコード スニペットです。

    @XmlJavaTypeAdapter(MapAdapter.class)
    Map<String, Map<String, Integer>> mapOfMap = new HashMap<String,Map<String, Integer>>();

MapType クラス:

public class MapType {

public List<MapEntryType> host = new ArrayList<MapEntryType>();

}

MapEntry タイプ クラス:

public class MapEntryType {

@XmlAttribute
public String ip;

@XmlElement
public List<LinkCountMapType> request_limit = new ArrayList<LinkCountMapType>();

}

LinkCountMapType クラス:

public class LinkCountMapType {
@XmlAttribute
public String service;

@XmlValue
public Integer count;
}

最後に MapAdaptor クラス:

    public final class MapAdapter extends XmlAdapter<MapType, Map<String, Map<String, Integer>>> {

@Override
public Map<String, Map<String, Integer>> unmarshal(MapType v) throws Exception {
    Map<String, Map<String, Integer>> mainMap = new HashMap<String, Map<String, Integer>>();

    List<MapEntryType> myMapEntryTypes = v.host;
    for (MapEntryType myMapEntryType : myMapEntryTypes) {
        Map<String, Integer> linkCountMap = new HashMap<String, Integer>();
        for (LinkCountMapType myLinkCountMapType : myMapEntryType.request_limit) {
            linkCountMap.put(myLinkCountMapType.service, myLinkCountMapType.count);
        }
        mainMap.put(myMapEntryType.ip, linkCountMap);
    }
    return mainMap;
}

@Override
public MapType marshal(Map<String, Map<String, Integer>> v) throws Exception {
    MapType myMapType = new MapType();

    List<MapEntryType> entry = new ArrayList<MapEntryType>();

    for (String ip : v.keySet()) {
        MapEntryType myMapEntryType = new MapEntryType();
        Map<String, Integer> linkCountMap = v.get(ip);
        List<LinkCountMapType> linkCountList = new ArrayList<LinkCountMapType>();
        for (String link : linkCountMap.keySet()) {
            LinkCountMapType myLinkCountMapType = new LinkCountMapType();
            Integer count = linkCountMap.get(link);
            myLinkCountMapType.count = count;
            myLinkCountMapType.service = link;
            linkCountList.add(myLinkCountMapType);
        }
        myMapEntryType.ip = ip;
        myMapEntryType.request_limit = linkCountList;
        entry.add(myMapEntryType);
    }
    myMapType.host = entry;
    return myMapType;
}

}

Jaxb オブジェクトをマーシャリングすると、以下の XML が得られます

     <mapOfmap>
    <host ip="127.0.0.1">
        <request_limit service="service1">7</request_limit>
        <request_limit service="service2">8</request_limit>
    </host>
</mapOfmap>
于 2012-04-16T14:33:08.093 に答える
2

以下は、上記の Ivan のコードに基づく「dexmlize」機能を備えたコードです。使用法:

Map<String, List> nameMapResult = (Map<String, List>) Adapters.dexmlizeNestedStructure(unmarshallResult);

コレクションとマップ クラスを復元​​するために、新しいフィールドが xml 化され、クラス情報が記録されます。詳細なコード:

class Adapters {
    private Adapters() {
    }
    public static Class<?>[] getXmlClasses() {
            return new Class<?>[]{XMap.class, XEntry.class, XCollection.class};
    }
    public static Object xmlizeNestedStructure(Object input) {
            if (input instanceof Map<?, ?>) {
                    return xmlizeNestedMap((Map<?, ?>) input);
            }
            if (input instanceof Collection<?>) {
                    return xmlizeNestedCollection((Collection<?>) input);
            }
            return input; // non-special object, return as is
    }

    public static Object dexmlizeNestedStructure(Object input) {
        if (input instanceof XMap<?, ?>) {
                return dexmlizeNestedMap((XMap<?, ?>) input);
        }
        if (input instanceof XCollection<?>) {
                return dexmlizeNestedCollection((XCollection<?>) input);
        }
        return input; // non-special object, return as is
    }

    private static Object dexmlizeNestedCollection(XCollection<?> input)
    {
        Class<? extends Collection> clazz = input.getClazz();
        Collection collection = null;
        try
        {
            collection = clazz.newInstance();
            List dataList = input.getList();
            for (Object object : dataList)
            {
                collection.add(dexmlizeNestedStructure(object));
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return collection;
    }

    private static Object dexmlizeNestedMap(XMap<?, ?> input)
    {
        Class<? extends Map> clazz = input.getClazz();
        Map map = null;
        try
        {
            map = clazz.newInstance();
            List<? extends XEntry> entryList = input.getList();
            for (XEntry xEntry : entryList)
            {
                Object key = dexmlizeNestedStructure(xEntry.getKey());
                Object value = dexmlizeNestedStructure(xEntry.getValue());
                map.put(key, value);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return map;
    }

    public static XMap<?, ?> xmlizeNestedMap(Map<?, ?> input) {
            XMap<Object, Object> ret = new XMap<Object, Object>(input.getClass());

            for (Map.Entry<?, ?> e : input.entrySet()) {
                    ret.add(xmlizeNestedStructure(e.getKey()),
                                    xmlizeNestedStructure(e.getValue()));
            }
            return ret;
    }

    public static XCollection<?> xmlizeNestedCollection(Collection<?> input) {
            XCollection<Object> ret = new XCollection<Object>(input.getClass());

            for (Object entry : input) {
                    ret.add(xmlizeNestedStructure(entry));
            }
            return ret;
    }

    @XmlType
    @XmlRootElement
    public final static class XMap<K, V>{
            private List<XEntry<K, V>> list = new ArrayList<XEntry<K, V>>();
            private Class<? extends Map> clazz = null;

            public XMap(Class mapClazz) {
                this.clazz = (Class<? extends Map>)mapClazz;
            }

            public XMap() {
            }

            public void add(K key, V value) {
                    list.add(new XEntry<K, V>(key, value));
            }

            @XmlElementWrapper(name = "map")
            @XmlElement(name = "entry")
            public List<XEntry<K, V>> getList()
            {
                return list;
            }

            public void setList(List<XEntry<K, V>> list)
            {
                this.list = list;
            }

            @XmlElement(name="clazz")
            public Class<? extends Map> getClazz()
            {
                return clazz;
            }

            public void setClazz(Class<? extends Map> clazz)
            {
                this.clazz = clazz;
            }
    }

    @XmlType
    @XmlRootElement
    public final static class XEntry<K, V> {
            private K key;
            private V value;

            private XEntry() {
            }

            public XEntry(K key, V value) {
                    this.key = key;
                    this.value = value;
            }

            @XmlElement
            public K getKey()
            {
                return key;
            }

            public void setKey(K key)
            {
                this.key = key;
            }

            @XmlElement
            public V getValue()
            {
                return value;
            }

            public void setValue(V value)
            {
                this.value = value;
            }
    }

    @XmlType
    @XmlRootElement
    public final static class XCollection<V> {
            private List<V> list = new ArrayList<V>();
            private Class<? extends Collection> clazz = null; 

            public XCollection(Class collectionClazz) {
                this.clazz = collectionClazz;
            }

            public XCollection() {
            }

            public void add(V obj) {
                    list.add(obj);
            }

            @XmlElementWrapper(name = "collection")
            @XmlElement(name = "entry")
            public List<V> getList()
            {
                return list;
            }

            public void setList(List<V> list)
            {
                this.list = list;
            }

            @XmlElement(name="clazz")
            public Class<? extends Collection> getClazz()
            {
                return clazz;
            }


            public void setClazz(Class<? extends Collection> clazz)
            {
                this.clazz = clazz;
            }
    }

}
于 2011-07-22T02:03:44.870 に答える
1

@XmlType クラスのリストのマーシャラー/アンマーシャラーは次のとおりです。

例えば

//Type to marshall
@XmlType(name = "TimecardForm", propOrder = {
"trackId",
"formId"
}) 
public class TimecardForm {

    protected long trackId;
    protected long formId;
    ...
}

//a list holder
@XmlRootElement
public class ListHodler<T> {
    @XmlElement
    private List<T> value ;

    public ListHodler() {
    }

    public ListHodler(List<T> value) {
        this.value = value;
    }

    public List<T> getValue() {
        if(value == null)
            value = new ArrayList<T>();
        return this.value;
    }
}

//marshall collection of T
public static <T> void marshallXmlTypeCollection(List<T> value,
        Class<T> clzz, OutputStream os) {
    try {
        ListHodler<T> holder = new ListHodler<T>(value);
        JAXBContext context = JAXBContext.newInstance(clzz,
                ListHodler.class);
        Marshaller m = context.createMarshaller();
        m.setProperty("jaxb.formatted.output", true);

        m.marshal(holder, os);
    } catch (JAXBException e) {
        e.printStackTrace();
    }
}

//unmarshall collection of T
@SuppressWarnings("unchecked")
public static <T> List<T> unmarshallXmlTypeCollection(Class<T> clzz,
        InputStream input) {
    try {
        JAXBContext context = JAXBContext.newInstance(ListHodler.class, clzz);
        Unmarshaller u = context.createUnmarshaller();

        ListHodler<T> holder = (ListHodler<T>) u.unmarshal(new StreamSource(input));

        return holder.getValue();
    } catch (JAXBException e) {
        e.printStackTrace();
    }

    return null;
}
于 2011-07-14T09:17:30.113 に答える
1

XMLAdapter で正しい方向に進んでいるようです...エラーメッセージが手がかりになるかもしれません:

class java.util.Collections$UnmodifiableMap も、そのスーパー クラスも、このコンテキストには認識されていません。

Collections.unmodifiableMap() を使用してマップをラップしていますか? エラーは正確にどこで発生しますか?


(以前の回答は、好奇心旺盛な人のために古い記録として残されています)

カスタム マーシャラー/アンマーシャラー ロジックを作成して、アダプターのアイデアよりも少し単純に機能させることができます (私はそう思いますが、以前は使用したことがありません)。

基本的には、作業を行う静的関数を指定し、カスタム クラスを作成することもできます。(私は通常、問題のクラスに静的関数を入れますが、そうする必要はありません。) 次に、.XJB ファイルに行を入れて、静的関数を使用するように JAXB に指示します。

既存のコードを見てみると、属性文字列をカスタム Java オブジェクトに変換するだけだったことがわかります。参照用のコードを次に示しますが、これは単なる属性用です。

JAXB ファイル:

<?xml version="1.0" ?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    jaxb:version="2.0"> 
    <jaxb:bindings schemaLocation={your schema} node="/xsd:schema">
        <jaxb:bindings node={some XPATH expression to select a node}>
            <jaxb:bindings node={maybe another XPATH relative to the above}>
                <jaxb:property>
                    <jaxb:baseType>
                        <jaxb:javaType name={your custom Java class}
                            parseMethod={your static method for unmarshaling}
                            printMethod={your static method for marshaling}
                            />
                    </jaxb:baseType>
                </jaxb:property>
            </jaxb:bindings>
        </jaxb:bindings>
    </jaxb:bindings>
</jaxb:bindings>

(parseMethod と printMethod は、属性文字列との間で変換されます)

于 2009-05-03T23:37:45.930 に答える
0

JSONでこれを修正するには、次のようにします 。jaxbを使用したjackson

<init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>
于 2012-06-29T15:14:34.697 に答える