iva*_*off 32 java xml data-binding jaxb data-structures
我有几个复杂的数据结构,如
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)
Run Code Online (Sandbox Code Playgroud)
注意:在我的情况下,如果我使用Set或List并不重要.
现在我知道JAXB允许我定义XmlAdapter,这没关系,但我不想为每个给定的数据结构定义一个XmlAdapter(它只是太多的复制和粘贴代码).
我试图通过声明两个泛化XmlAdapter来实现我的目标:
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.
Run Code Online (Sandbox Code Playgroud)
这是我的适配器类:
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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的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());
}
}
Run Code Online (Sandbox Code Playgroud)
iva*_*off 21
我已经解决了没有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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
有用!
我们来看一个演示输出:
<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>
Run Code Online (Sandbox Code Playgroud)
对不起,演示输出还使用了一个名为"count"的数据结构 ,这在Adapter的源代码中没有提到.
BTW:有没有人知道如何删除所有这些烦人的(在我的情况下)不必要的xsi:type属性?
小智 5
我有同样的要求使用Map <String,Map <String,Integer >>.我使用了XMLAdapter,它运行良好.使用XMLAdaptor是我认为最干净的解决方案.下面是适配器的代码.这是jaXb类代码片段.
@XmlJavaTypeAdapter(MapAdapter.class)
Map<String, Map<String, Integer>> mapOfMap = new HashMap<String,Map<String, Integer>>();
Run Code Online (Sandbox Code Playgroud)
MapType类:
public class MapType {
public List<MapEntryType> host = new ArrayList<MapEntryType>();
}
Run Code Online (Sandbox Code Playgroud)
MapEntry类型类:
public class MapEntryType {
@XmlAttribute
public String ip;
@XmlElement
public List<LinkCountMapType> request_limit = new ArrayList<LinkCountMapType>();
}
Run Code Online (Sandbox Code Playgroud)
LinkCountMapType类:
public class LinkCountMapType {
@XmlAttribute
public String service;
@XmlValue
public Integer count;
}
Run Code Online (Sandbox Code Playgroud)
最后是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;
}
Run Code Online (Sandbox Code Playgroud)
}
编组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>
Run Code Online (Sandbox Code Playgroud)