我想将Map编组/解组为XML元素的属性。我见过这样的例子:
<map>
<entry key="key1">value1</entry>
<entry key="key2">value2</entry>
</map>
我真正想要的是:
<map key1="value1" key2="value2"/>
假设没有复杂的值,并且它们可以合法地表示为XML属性。此外,我正在尝试一般性地编写这个,因为直到运行时才知道密钥集。
我该怎么做?我熟悉XmlJavaTypeAdapter。
我考虑创建一个包含条目列表的MyMap,但这不会得到我想要的输出。
答案 0 :(得分:1)
就像我在评论中暗示的那样,单靠JAXB就无法做到这一点。在JAXB规范(JSR 222)中,它说:
在所有应用程序场景中,我们创建架构的Java对象级绑定。
这意味着绑定的范围与模式的范围相同,这是静态的。如果不重新编译代码,则无意更改JAXB绑定。有一些例外,例如对于xs:anyAttribute
,在规范的第6.9节中讨论过,但由于您没有投票赞成建议使用@XmlAnyAttribute
的答案,您可能不希望遇到这些限制 - 例如地图中只有QName
个键。
我希望你确信用JAXB做你想做的事是一个非常糟糕的主意,但仅供参考,下面是一个在编组后修改文档以将其带到你想要的结构的例子。您可以将其复制并粘贴到单个文件中,然后使用Java 7进行编译。输出将如下所示:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<mapExample>
<map France="Paris" Japan="Tokyo"/>
</mapExample>
我的代码只显示另一个方向的marshalilng:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@XmlRootElement
class MapExample {
@XmlJavaTypeAdapter(MapXmlAdapter.class)
@XmlElement(name="map")
private Map<String, String> data = new HashMap<>();
public static void main(String[] args) throws Exception {
MapExample example = new MapExample();
example.data.put("France", "Paris");
example.data.put("Japan", "Tokyo");
JAXBContext context = JAXBContext.newInstance(MapExample.class);
Marshaller marshaller = context.createMarshaller();
DOMResult result = new DOMResult();
marshaller.marshal(example, result);
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
Document document = (Document)result.getNode();
XPathExpression expression = xpath.compile("//map/entry");
NodeList nodes = (NodeList)expression.evaluate(document, XPathConstants.NODESET);
expression = xpath.compile("//map");
Node oldMap = (Node)expression.evaluate(document, XPathConstants.NODE);
Element newMap = document.createElement("map");
for (int index = 0; index < nodes.getLength(); index++) {
Element element = (Element)nodes.item(index);
newMap.setAttribute(element.getAttribute("key"),
element.getAttribute("value"));
}
expression = xpath.compile("//map/..");
Node parent = (Node)expression.evaluate(document, XPathConstants.NODE);
parent.replaceChild(newMap, oldMap);
TransformerFactory.newInstance().newTransformer().
transform(new DOMSource(document), new StreamResult(System.out));
}
}
class MapXmlAdapter extends XmlAdapter<MyMap, Map<String, String>> {
@Override
public Map<String, String> unmarshal(MyMap value) throws Exception {
throw new UnsupportedOperationException();
}
@Override
public MyMap marshal(Map<String, String> value) throws Exception {
MyMap map = new MyMap();
map.entries = new ArrayList<MyEntry>();
for (String key : value.keySet()) {
MyEntry entry = new MyEntry();
entry.key = key;
entry.value = value.get(key);
map.entries.add(entry);
}
return map;
}
}
class MyMap {
@XmlElement(name="entry")
public List<MyEntry> entries;
}
class MyEntry {
@XmlAttribute
public String key;
@XmlAttribute
public String value;
}
答案 1 :(得分:-1)
这听起来像是@XmlAnyAttribute
的用途。您可以将该注释放在Map<QName, Object>
上,它会将未被其他注释明确约束的所有属性收集到该地图中。
@XmlRootElement
public class Example {
@XmlElement(name = "map")
@XmlJavaTypeAdapter(MapAdapter.class)
private Map<String, String> map;
}
class MapAdapter extends XmlAdapter<MapWrapper, Map<String, String>> {
@Override
public Map<String, String> unmarshal(MapWrapper value) throws Exception {
if(value == null || value.attributes == null) return null;
Map<String, String> map = new HashMap<String, String>();
for(Map.Entry<QName, Object> entry : value.attributes.entrySet()) {
map.put(entry.getKey().getLocalPart(), entry.getValue().toString());
}
return map;
}
@Override
public MapWrapper marshal(Map<String, String> map) throws Exception {
if(map == null) return null;
MapWrapper w = new MapWrapper();
w.attributes = new HashMap<QName, Object>();
for (Map.Entry<String, String> entry : map.entrySet()) {
w.attributes.put(new QName(entry.getKey()), entry.getValue());
}
return w;
}
}
class MapWrapper {
@XmlAnyAttribute
public Map<QName, Object> attributes;
}