JAXB java.util.Map 到键值对
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12901302/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JAXB java.util.Map to key value pairs
提问by bmauter
I'd like to marshall/unmarshall a Map into attributes of an XML element. I've seen examples like:
我想将 Map 编组/解组为 XML 元素的属性。我见过这样的例子:
<map>
<entry key="key1">value1</entry>
<entry key="key2">value2</entry>
</map>
What I really want is:
我真正想要的是:
<map key1="value1" key2="value2"/>
Assume with me that there are no complex values and that they can legally be represented as XML attributes. Also, I'm trying to write this generically because the set of keys is not known until runtime.
和我一起假设没有复杂的值,并且它们可以合法地表示为 XML 属性。另外,我试图一般地编写它,因为直到运行时才知道密钥集。
How would I go about this? I'm familiar with XmlJavaTypeAdapter.
我该怎么办?我熟悉 XmlJavaTypeAdapter。
I thought about creating a MyMap that contains a List of entries but this wouldn't get the output I'd like.
我想过创建一个包含条目列表的 MyMap 但这不会得到我想要的输出。
采纳答案by Tilo
Like I hinted in my comment, this cannot be achieved with JAXB alone. In the JAXB specification (JSR 222) it says:
就像我在评论中暗示的那样,仅靠 JAXB 无法实现这一点。在 JAXB 规范 ( JSR 222) 中,它说:
In all application scenarios, we create a Java object-level binding of the schema.
在所有应用场景中,我们都会创建架构的 Java 对象级绑定。
That means that the scope of the binding is the same as the scope of the schema, which is static. A JAXB binding is not meant to be changed without recompiling the code. There are some exceptions, e.g. for xs:anyAttribute
which is discussed in section 6.9 of the specification, but since you didn't vote for the answer suggesting the use of @XmlAnyAttribute
you probably don't want to live with the limitations - e.g. only have QName
keys in your map.
这意味着绑定的范围与架构的范围相同,后者是静态的。不重新编译代码就不能更改 JAXB 绑定。有一些例外,例如xs:anyAttribute
在规范的第 6.9 节中讨论的例外,但是由于您没有投票支持建议使用的答案,@XmlAnyAttribute
您可能不想忍受这些限制 - 例如QName
,您的地图中只有键.
I hope you are convinced that to do what you want with JAXB is a really bad idea, but just for reference below is an example that modifies the document after marshalling to bring it to the structure you want. You can copy and paste it into a single file and compile it with Java 7. The output will look like this:
我希望您确信使用 JAXB 做您想做的事情是一个非常糟糕的主意,但仅供参考,下面是一个示例,该示例在编组后修改文档以使其成为您想要的结构。您可以将其复制并粘贴到单个文件中,然后使用 Java 7 进行编译。输出将如下所示:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<mapExample>
<map France="Paris" Japan="Tokyo"/>
</mapExample>
My code only shows the marshalilng the other direction is equivalent:
我的代码只显示 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;
}
回答by Ian Roberts
This sounds like a use for @XmlAnyAttribute
. You can put that annotation on a Map<QName, Object>
and it will gather all the attributes not explicitly bound by other annotations into that map.
这听起来像是用于@XmlAnyAttribute
. 您可以将该注释放在 a 上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;
}