私はいくつかの複雑なデータ構造を持っています
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を宣言することで目標を達成しようとしました。
MapAdapter<K,V>
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());
}
}
私は問題を解決しましたXmlAdapterなし。
Map、Map.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"というデータ構造も使用されますが、これはアダプタのソースコードには記載されていません。
BTW:これらの迷惑な(私の場合は)不要なすべての属性を削除する方法を誰もが知っていますか?xsi:type
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>
以下は、上記の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;
}
}
}
複雑なスキーマ構造を使用する場合-競合するオブジェクトを解決するには、JAXBバインディングが重要になる場合があります。 SourceforgeのCAM Editorツールを使用すると、JAXBバインディングを自動的に作成できます-詳細については、こちらのクイックガイドを参照してください- http://www.cameditor.org/#JAXB_Bindings
JSONでこれを修正するには: jackson with jaxb
<init-param>
<param-name>com.Sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
@XmlTypeクラスのリストの私のmarshaller/unmarshallerを以下に示します。
例えば
//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;
}
XMLAdapterで正しい軌道に乗っているように見えます...エラーメッセージが手掛かりかもしれません:
java.util.Collections $ UnmodifiableMapクラスもそのスーパークラスもこのコンテキストで認識されています。
collections.unmodifiableMap()を使用してどこでもマップをラップしていますか?エラーはどこで発生しますか?
(好奇心のための古い記録として残された以前の回答)
Adaptersのアイデアよりも少し簡単に動作するカスタムマーシャラー/アンマーシャラーロジックを作成できます(以前は使用していなかったと思います)。
基本的には、静的関数を指定して作業を行うという考え方であり、カスタムクラスを作成することもできます。 (通常、問題のクラスに静的関数を配置しますが、その必要はありません。)次に、.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は属性文字列との間で変換します)