JAXB:我应该如何编组复杂的嵌套数据结构?(JAXB: How should I marshal complex nested data structures?)

编程入门 行业动态 更新时间:2024-10-05 13:20:51
JAXB:我应该如何编组复杂的嵌套数据结构?(JAXB: How should I marshal complex nested 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)

注意:就我而言,使用Set或List并不重要。

现在我知道JAXB让我定义XmlAdapter的,这很好,但我不想为每个给定的数据结构定义一个XmlAdapter(它只是太多的复制和粘贴代码)。

我试图通过声明两个一般化的XmlAdapter来实现我的目标:

一个用于Map: MapAdapter<K,V> 一个用于Set: 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()); } }

I have several complex data structures like

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)

Note: In my case it doesn't really matter if I use Set or List.

Now I know that JAXB let me define XmlAdapter's, that's fine, but I don't want to define an XmlAdapter for every of the given data structures (it would be just too much copy-and-paste code).

I tried to achieve my goal by declaring two generalizing XmlAdapters:

one for Map: MapAdapter<K,V> one for Set: SetAdapter<V>

The problem: JAXB complains as following:

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

Here is my adapter class:

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"); } } }

Here is my JUnit test case:

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的问题。

我为MapMap.EntryCollection编写了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属性?

I've solved the problem without XmlAdapter's.

I've written JAXB-annotated objects for Map, Map.Entry and Collection. The main idea is inside the method xmlizeNestedStructure(...):

Take a look at the code:

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); } } }

It works!

Let's look at a demo output:

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

Sorry, the demo output uses also a data structure called "count" which is not mentioned in the Adapter's source code.

BTW: does anyone know how to remove all these annoying and (in my case) unnecessary xsi:type attributes?

更多推荐

本文发布于:2023-08-07 17:27:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1465233.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:嵌套   数据结构   marshal   JAXB   data

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!