Tim*_*mur 64 java xml jaxb jaxb2
问题是关于JAXB Map编组 - 有很多关于如何将Map转换为如下结构的示例:
<map>
<entry>
<key> KEY </key>
<value> VALUE </value>
</entry>
<entry>
<key> KEY2 </key>
<value> VALUE2 </value>
</entry>
<entry>
...
</map>
Run Code Online (Sandbox Code Playgroud)
实际上,这是JAXB原生支持的.但是,我需要的是XML,其中key是元素名称,value是其内容:
<map>
<key> VALUE </key>
<key2> VALUE2 </key2>
...
</map>
Run Code Online (Sandbox Code Playgroud)
我没有按照JAXB开发人员推荐的方式(https://jaxb.dev.java.net/guide/Mapping_your_favorite_class.html)成功实现我的Map适配器,因为我需要,他 - 动态属性名称:)
那有什么解决方案吗?
PS目前我必须为每个我想要编组的典型键值对创建一个专用的容器类 - 它可以工作,但是我必须创建太多这些辅助容器.
Jus*_*owe 28
您可能有正当理由要这样做,但通常最好避免生成此类XML.为什么?因为这意味着地图的XML元素依赖于地图的运行时内容.由于XML通常用作外部接口或接口层,因此这是不可取的.让我解释.
Xml Schema(xsd)定义XML文档的接口契约.除了能够从XSD生成代码之外,JAXB还可以从代码中为您生成XML模式.这允许您将通过接口交换的数据限制为XSD中定义的预先约定的结构.
在a的默认情况下Map<String, String>,生成的XSD将限制map元素包含多个条目元素,每个条目元素必须包含一个xs:string键和一个xs:string值.这是一个非常明确的接口合同.
您所描述的是您希望xml映射包含其名称将在运行时由地图内容确定的元素.然后生成的XSD只能指定映射必须包含在编译时类型未知的元素列表.在定义接口契约时,您通常应该避免这种情况.
要在这种情况下实现严格的约定,您应该使用枚举类型作为映射的键而不是String.例如
public enum KeyType {
KEY, KEY2;
}
@XmlJavaTypeAdapter(MapAdapter.class)
Map<KeyType , String> mapProperty;
Run Code Online (Sandbox Code Playgroud)
这样,您希望在XML中成为XML元素的键在编译时就已知,因此JAXB应该能够生成一个模式,该模式将使用预定义键KEY或KEY2之一将map元素限制为元素.
另一方面,如果您希望简化默认生成的结构
<map>
<entry>
<key>KEY</key>
<value>VALUE</value>
</entry>
<entry>
<key>KEY2</key>
<value>VALUE2</value>
</entry>
</map>
Run Code Online (Sandbox Code Playgroud)
对于这样简单的事情
<map>
<item key="KEY" value="VALUE"/>
<item key="KEY2" value="VALUE2"/>
</map>
Run Code Online (Sandbox Code Playgroud)
您可以使用MapAdapter将Map转换为MapElements数组,如下所示:
class MapElements {
@XmlAttribute
public String key;
@XmlAttribute
public String value;
private MapElements() {
} //Required by JAXB
public MapElements(String key, String value) {
this.key = key;
this.value = value;
}
}
public class MapAdapter extends XmlAdapter<MapElements[], Map<String, String>> {
public MapAdapter() {
}
public MapElements[] marshal(Map<String, String> arg0) throws Exception {
MapElements[] mapElements = new MapElements[arg0.size()];
int i = 0;
for (Map.Entry<String, String> entry : arg0.entrySet())
mapElements[i++] = new MapElements(entry.getKey(), entry.getValue());
return mapElements;
}
public Map<String, String> unmarshal(MapElements[] arg0) throws Exception {
Map<String, String> r = new TreeMap<String, String>();
for (MapElements mapelement : arg0)
r.put(mapelement.key, mapelement.value);
return r;
}
}
Run Code Online (Sandbox Code Playgroud)
Gré*_*ory 22
提供的代码对我不起作用.我发现了另一种Map方式:
MapElements:
package com.cellfish.mediadb.rest.lucene;
import javax.xml.bind.annotation.XmlElement;
class MapElements
{
@XmlElement public String key;
@XmlElement public Integer value;
private MapElements() {} //Required by JAXB
public MapElements(String key, Integer value)
{
this.key = key;
this.value = value;
}
}
Run Code Online (Sandbox Code Playgroud)
MapAdapter:
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.adapters.XmlAdapter;
class MapAdapter extends XmlAdapter<MapElements[], Map<String, Integer>> {
public MapElements[] marshal(Map<String, Integer> arg0) throws Exception {
MapElements[] mapElements = new MapElements[arg0.size()];
int i = 0;
for (Map.Entry<String, Integer> entry : arg0.entrySet())
mapElements[i++] = new MapElements(entry.getKey(), entry.getValue());
return mapElements;
}
public Map<String, Integer> unmarshal(MapElements[] arg0) throws Exception {
Map<String, Integer> r = new HashMap<String, Integer>();
for (MapElements mapelement : arg0)
r.put(mapelement.key, mapelement.value);
return r;
}
}
Run Code Online (Sandbox Code Playgroud)
rootElement:
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class Root {
private Map<String, Integer> mapProperty;
public Root() {
mapProperty = new HashMap<String, Integer>();
}
@XmlJavaTypeAdapter(MapAdapter.class)
public Map<String, Integer> getMapProperty() {
return mapProperty;
}
public void setMapProperty(Map<String, Integer> map) {
this.mapProperty = map;
}
}
Run Code Online (Sandbox Code Playgroud)
我在这个网站上找到了代码:http: //www.developpez.net/forums/d972324/java/general-java/xml/hashmap-jaxb/
bdo*_*han 15
我仍在研究更好的解决方案,但使用MOXy JAXB,我已经能够处理以下XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<mapProperty>
<map>
<key>value</key>
<key2>value2</key2>
</map>
</mapProperty>
</root>
Run Code Online (Sandbox Code Playgroud)
您需要在Map属性上使用@XmlJavaTypeAdapter:
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class Root {
private Map<String, String> mapProperty;
public Root() {
mapProperty = new HashMap<String, String>();
}
@XmlJavaTypeAdapter(MapAdapter.class)
public Map<String, String> getMapProperty() {
return mapProperty;
}
public void setMapProperty(Map<String, String> map) {
this.mapProperty = map;
}
}
Run Code Online (Sandbox Code Playgroud)
XmlAdapter的实现如下:
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class MapAdapter extends XmlAdapter<AdaptedMap, Map<String, String>> {
@Override
public AdaptedMap marshal(Map<String, String> map) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
Element rootElement = document.createElement("map");
document.appendChild(rootElement);
for(Entry<String,String> entry : map.entrySet()) {
Element mapElement = document.createElement(entry.getKey());
mapElement.setTextContent(entry.getValue());
rootElement.appendChild(mapElement);
}
AdaptedMap adaptedMap = new AdaptedMap();
adaptedMap.setValue(document);
return adaptedMap;
}
@Override
public Map<String, String> unmarshal(AdaptedMap adaptedMap) throws Exception {
Map<String, String> map = new HashMap<String, String>();
Element rootElement = (Element) adaptedMap.getValue();
NodeList childNodes = rootElement.getChildNodes();
for(int x=0,size=childNodes.getLength(); x<size; x++) {
Node childNode = childNodes.item(x);
if(childNode.getNodeType() == Node.ELEMENT_NODE) {
map.put(childNode.getLocalName(), childNode.getTextContent());
}
}
return map;
}
}
Run Code Online (Sandbox Code Playgroud)
AdpatedMap类是所有魔术发生的地方,我们将使用DOM来表示内容.我们将通过组合@XmlAnyElement和Object类型的属性来欺骗JAXB介绍处理DOM:
import javax.xml.bind.annotation.XmlAnyElement;
public class AdaptedMap {
private Object value;
@XmlAnyElement
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
Run Code Online (Sandbox Code Playgroud)
此解决方案需要MOXy JAXB实现.您可以通过使用以下条目在模型类中添加名为jaxb.properties的文件来配置JAXB运行时以使用MOXy实现:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Run Code Online (Sandbox Code Playgroud)
以下演示代码可用于验证代码:
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Root root = (Root) unmarshaller.unmarshal(new File("src/forum74/input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Run Code Online (Sandbox Code Playgroud)
小智 10
我没有看到任何真正回答这个问题的事情.我找到了一些在这里工作得很好的东西:
使用JAXB XMLAnyElement类型的样式来返回动态元素名称
我修改了一下以支持hashmap树.您可以添加其他集合.
public class MapAdapter extends XmlAdapter<MapWrapper, Map<String, Object>> {
@Override
public MapWrapper marshal(Map<String, Object> m) throws Exception {
MapWrapper wrapper = new MapWrapper();
List elements = new ArrayList();
for (Map.Entry<String, Object> property : m.entrySet()) {
if (property.getValue() instanceof Map)
elements.add(new JAXBElement<MapWrapper>(new QName(getCleanLabel(property.getKey())),
MapWrapper.class, marshal((Map) property.getValue())));
else
elements.add(new JAXBElement<String>(new QName(getCleanLabel(property.getKey())),
String.class, property.getValue().toString()));
}
wrapper.elements = elements;
return wrapper;
}
@Override
public Map<String, Object> unmarshal(MapWrapper v) throws Exception {
// TODO
throw new OperationNotSupportedException();
}
// Return a XML-safe attribute. Might want to add camel case support
private String getCleanLabel(String attributeLabel) {
attributeLabel = attributeLabel.replaceAll("[()]", "").replaceAll("[^\\w\\s]", "_").replaceAll(" ", "_");
return attributeLabel;
}
}
class MapWrapper {
@XmlAnyElement
List elements;
}
Run Code Online (Sandbox Code Playgroud)
然后实现它:
static class myxml {
String name = "Full Name";
String address = "1234 Main St";
// I assign values to the map elsewhere, but it's just a simple
// hashmap with a hashmap child as an example.
@XmlJavaTypeAdapter(MapAdapter.class)
public Map<String, Object> childMap;
}
Run Code Online (Sandbox Code Playgroud)
通过一个简单的Marshaller提供这个输出,看起来像这样:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myxml>
<name>Full Name</name>
<address>1234 Main St</address>
<childMap>
<key2>value2</key2>
<key1>value1</key1>
<childTree>
<childkey1>childvalue1</childkey1>
</childTree>
</childMap>
</myxml>
Run Code Online (Sandbox Code Playgroud)