我正在搜索轻量级API(首选单个类)来转换
Map<String,String> map = new HashMap<String,String();
到xml,反之亦然,将XML转换回Map。
示例:
Map<String,String> map = new HashMap<String,String();
map.put("name","chris");
map.put("island","faranga");
MagicAPI.toXML(map,"root");
结果:
<root>
<name>chris</chris>
<island>faranga</island>
</root>
并返回:
Map<String,String> map = MagicAPI.fromXML("...");
我不想使用JAXB或JSON conversion API。它不需要处理嵌套的地图或属性或其他任何东西,只是这个简单的情况。 有什么建议吗?
修改:我创建了一份工作副本&amp;粘贴样品。感谢fvu和Michal Bernhard。
Download latest XStream framework,“仅核心”就足够了。
Map<String,Object> map = new HashMap<String,Object>();
map.put("name","chris");
map.put("island","faranga");
// convert to XML
XStream xStream = new XStream(new DomDriver());
xStream.alias("map", java.util.Map.class);
String xml = xStream.toXML(map);
// from XML, convert back to map
Map<String,Object> map2 = (Map<String,Object>) xStream.fromXML(xml);
不需要转换器或其他任何东西。只需xstream-x.y.z.jar即可。
答案 0 :(得分:61)
的XStream!
已更新:我在评论中添加了非编组部分..
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put("name","chris");
map.put("island","faranga");
XStream magicApi = new XStream();
magicApi.registerConverter(new MapEntryConverter());
magicApi.alias("root", Map.class);
String xml = magicApi.toXML(map);
System.out.println("Result of tweaked XStream toXml()");
System.out.println(xml);
Map<String, String> extractedMap = (Map<String, String>) magicApi.fromXML(xml);
assert extractedMap.get("name").equals("chris");
assert extractedMap.get("island").equals("faranga");
}
public static class MapEntryConverter implements Converter {
public boolean canConvert(Class clazz) {
return AbstractMap.class.isAssignableFrom(clazz);
}
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
AbstractMap map = (AbstractMap) value;
for (Object obj : map.entrySet()) {
Map.Entry entry = (Map.Entry) obj;
writer.startNode(entry.getKey().toString());
Object val = entry.getValue();
if ( null != val ) {
writer.setValue(val.toString());
}
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
Map<String, String> map = new HashMap<String, String>();
while(reader.hasMoreChildren()) {
reader.moveDown();
String key = reader.getNodeName(); // nodeName aka element's name
String value = reader.getValue();
map.put(key, value);
reader.moveUp();
}
return map;
}
}
}
答案 1 :(得分:43)
此处XStream的转换器包括unmarshall
public class MapEntryConverter implements Converter{
public boolean canConvert(Class clazz) {
return AbstractMap.class.isAssignableFrom(clazz);
}
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
AbstractMap<String,String> map = (AbstractMap<String,String>) value;
for (Entry<String,String> entry : map.entrySet()) {
writer.startNode(entry.getKey().toString());
writer.setValue(entry.getValue().toString());
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
Map<String, String> map = new HashMap<String, String>();
while(reader.hasMoreChildren()) {
reader.moveDown();
map.put(reader.getNodeName(), reader.getValue());
reader.moveUp();
}
return map;
}
答案 2 :(得分:8)
XStream怎么样?对于包括你在内的许多用例而言,不是1级而是2个罐子,使用起来非常简单但非常强大。
答案 3 :(得分:8)
一种选择是自己动手。这样做很简单:
Document doc = getDocument();
Element root = doc.createElement(rootName);
doc.appendChild(root);
for (Map.Entry<String,String> element : map.entrySet() ) {
Element e = doc.createElement(element.getKey());
e.setTextContent(element.getValue());
root.appendChild(e);
}
save(doc, file);
并且负载同样简单getChildNodes
和循环。当然它有一些XML神需要的锅炉板,但它最多工作1小时。
或者,如果您对XML的格式不太了解,可以查看Properties。
答案 4 :(得分:6)
我使用自定义转换器的方法:
public static class MapEntryConverter implements Converter {
public boolean canConvert(Class clazz) {
return AbstractMap.class.isAssignableFrom(clazz);
}
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
AbstractMap map = (AbstractMap) value;
for (Object obj : map.entrySet()) {
Entry entry = (Entry) obj;
writer.startNode(entry.getKey().toString());
context.convertAnother(entry.getValue());
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
// dunno, read manual and do it yourself ;)
}
}
但是我将maps值的序列化更改为委托给MarshallingContext。这应该改进解决方案以适用于复合地图值和嵌套地图。
答案 5 :(得分:3)
我编写了一段代码,将XML内容转换为多层地图结构:
public static Object convertNodesFromXml(String xml) throws Exception {
InputStream is = new ByteArrayInputStream(xml.getBytes());
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(is);
return createMap(document.getDocumentElement());
}
public static Object createMap(Node node) {
Map<String, Object> map = new HashMap<String, Object>();
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node currentNode = nodeList.item(i);
String name = currentNode.getNodeName();
Object value = null;
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
value = createMap(currentNode);
}
else if (currentNode.getNodeType() == Node.TEXT_NODE) {
return currentNode.getTextContent();
}
if (map.containsKey(name)) {
Object os = map.get(name);
if (os instanceof List) {
((List<Object>)os).add(value);
}
else {
List<Object> objs = new LinkedList<Object>();
objs.add(os);
objs.add(value);
map.put(name, objs);
}
}
else {
map.put(name, value);
}
}
return map;
}
此代码转换为:
<house>
<door>blue</door>
<living-room>
<table>wood</table>
<chair>wood</chair>
</living-room>
</house>
到
{
"house": {
"door": "blue",
"living-room": {
"table": "wood",
"chair": "wood"
}
}
}
我没有逆过程,但写起来一定不是很难。
答案 6 :(得分:2)
我发布这个作为答案不是因为它是你问题的正确答案,而是因为它是同一问题的解决方案,而是使用属性。否则Vikas Gujjar的回答是正确的。
你的数据很可能属于属性,但很难找到使用XStream执行此操作的任何工作示例,所以这里有一个:
示例数据:
<settings>
<property name="prop1" value="foo"/>
<property name="prop2" /> <!-- NOTE:
The example supports null elements as
the backing object is a HashMap.
A Properties object would be handled
by a PropertiesConverter which wouldn't
allow you null values. -->
<property name="prop3" value="1"/>
</settings>
MapEntryConverter的实现(稍微改编一下@Vikas Gujjar的实现来改为使用属性):
public class MapEntryConverter
implements Converter
{
public boolean canConvert(Class clazz)
{
return AbstractMap.class.isAssignableFrom(clazz);
}
public void marshal(Object value,
HierarchicalStreamWriter writer,
MarshallingContext context)
{
//noinspection unchecked
AbstractMap<String, String> map = (AbstractMap<String, String>) value;
for (Map.Entry<String, String> entry : map.entrySet())
{
//noinspection RedundantStringToString
writer.startNode(entry.getKey().toString());
//noinspection RedundantStringToString
writer.setValue(entry.getValue().toString());
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context)
{
Map<String, String> map = new HashMap<String, String>();
while (reader.hasMoreChildren())
{
reader.moveDown();
map.put(reader.getAttribute("name"), reader.getAttribute("value"));
reader.moveUp();
}
return map;
}
}
XStream实例设置,解析和存储:
XStream xstream = new XStream();
xstream.autodetectAnnotations(true);
xstream.alias("settings", HashMap.class);
xstream.registerConverter(new MapEntryConverter());
...
// Parse:
YourObject yourObject = (YourObject) xstream.fromXML(is);
// Store:
xstream.toXML(yourObject);
...
答案 7 :(得分:2)
我在google上发现了这个,但我不想使用XStream,因为它会导致我的环境中出现很多开销。我只需要解析一个文件,因为我没有找到任何我喜欢的东西,所以我创建了自己的简单解决方案来解析你描述的格式的文件。所以这是我的解决方案:
public class XmlToMapUtil {
public static Map<String, String> parse(InputSource inputSource) throws SAXException, IOException, ParserConfigurationException {
final DataCollector handler = new DataCollector();
SAXParserFactory.newInstance().newSAXParser().parse(inputSource, handler);
return handler.result;
}
private static class DataCollector extends DefaultHandler {
private final StringBuilder buffer = new StringBuilder();
private final Map<String, String> result = new HashMap<String, String>();
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
final String value = buffer.toString().trim();
if (value.length() > 0) {
result.put(qName, value);
}
buffer.setLength(0);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
buffer.append(ch, start, length);
}
}
}
以下是一些TestNG + FEST断言测试:
public class XmlToMapUtilTest {
@Test(dataProvider = "provide_xml_entries")
public void parse_returnsMapFromXml(String xml, MapAssert.Entry[] entries) throws Exception {
// execution
final Map<String, String> actual = XmlToMapUtil.parse(new InputSource(new StringReader(xml)));
// evaluation
assertThat(actual)
.includes(entries)
.hasSize(entries.length);
}
@DataProvider
public Object[][] provide_xml_entries() {
return new Object[][]{
{"<root />", new MapAssert.Entry[0]},
{
"<root><a>aVal</a></root>", new MapAssert.Entry[]{
MapAssert.entry("a", "aVal")
},
},
{
"<root><a>aVal</a><b>bVal</b></root>", new MapAssert.Entry[]{
MapAssert.entry("a", "aVal"),
MapAssert.entry("b", "bVal")
},
},
{
"<root> \t <a>\taVal </a><b /></root>", new MapAssert.Entry[]{
MapAssert.entry("a", "aVal")
},
},
};
}
}
答案 8 :(得分:1)
我尝试了不同类型的地图,Conversion Box也有效。我已经使用了你的地图并在下面粘贴了一些内部地图。希望它对你有所帮助....
import java.util.HashMap;
import java.util.Map;
import cjm.component.cb.map.ToMap;
import cjm.component.cb.xml.ToXML;
public class Testing
{
public static void main(String[] args)
{
try
{
Map<String, Object> map = new HashMap<String, Object>(); // ORIGINAL MAP
map.put("name", "chris");
map.put("island", "faranga");
Map<String, String> mapInner = new HashMap<String, String>(); // SAMPLE INNER MAP
mapInner.put("a", "A");
mapInner.put("b", "B");
mapInner.put("c", "C");
map.put("innerMap", mapInner);
Map<String, Object> mapRoot = new HashMap<String, Object>(); // ROOT MAP
mapRoot.put("ROOT", map);
System.out.println("Map: " + mapRoot);
System.out.println();
ToXML toXML = new ToXML();
String convertedXML = String.valueOf(toXML.convertToXML(mapRoot, true)); // CONVERTING ROOT MAP TO XML
System.out.println("Converted XML: " + convertedXML);
System.out.println();
ToMap toMap = new ToMap();
Map<String, Object> convertedMap = toMap.convertToMap(convertedXML); // CONVERTING CONVERTED XML BACK TO MAP
System.out.println("Converted Map: " + convertedMap);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
输出:
Map: {ROOT={name=chris, innerMap={b=B, c=C, a=A}, island=faranga}}
-------- Map Detected --------
-------- XML created Successfully --------
Converted XML: <ROOT><name>chris</name><innerMap><b>B</b><c>C</c><a>A</a></innerMap><island>faranga</island></ROOT>
-------- XML Detected --------
-------- Map created Successfully --------
Converted Map: {ROOT={name=chris, innerMap={b=B, c=C, a=A}, island=faranga}}
答案 9 :(得分:1)
Underscore-java库可以将Map转换为xml。我是该项目的维护者。 Live example
代码示例:
import com.github.underscore.lodash.U;
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("name", "chris");
map.put("island", "faranga");
System.out.println(U.toXml(map));
// <?xml version="1.0" encoding="UTF-8"?>
// <root>
// <name>chris</name>
// <island>faranga</island>
// </root>
// and back:
map = (Map<String, Object>) U.fromXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>"
+ " <name>chris</name>"
+ " <island>faranga</island>"
+ " </root>");
System.out.println(map);
// {name=chris, island=faranga}
}
}
答案 10 :(得分:1)
如果您只需要将简单地图转换为xml,而没有嵌套属性,那么轻量级解决方案将只是一个私有方法,如下所示:
private String convertMapToXML(Map<String, String> map) {
StringBuilder xmlBuilder = new StringBuilder();
xmlBuilder.append("<xml>");
for (Map.Entry<String, String> entry : map.entrySet()) {
if (entry.getValue() != null) {
String xmlElement = entry.getKey();
xmlBuilder.append("<");
xmlBuilder.append(xmlElement);
xmlBuilder.append(">");
xmlBuilder.append(entry.getValue());
xmlBuilder.append("<");
xmlBuilder.append("/");
xmlBuilder.append(xmlElement);
xmlBuilder.append(">");
}
}
xmlBuilder.append("</xml>");
return xmlBuilder.toString();
}
答案 11 :(得分:0)
现在是2017年,最新版本的XStream需要一个转换器才能使其按预期工作。
转换器支持嵌套映射:
public class MapEntryConverter implements Converter {
@Override
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext marshallingContext) {
AbstractMap map = (AbstractMap) value;
for (Object obj : map.entrySet()) {
Map.Entry entry = (Map.Entry) obj;
writer.startNode(entry.getKey().toString());
Object val = entry.getValue();
if (val instanceof Map) {
marshal(val, writer, marshallingContext);
} else if (null != val) {
writer.setValue(val.toString());
}
writer.endNode();
}
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext unmarshallingContext) {
Map<String, Object> map = new HashMap<>();
while(reader.hasMoreChildren()) {
reader.moveDown();
String key = reader.getNodeName(); // nodeName aka element's name
String value = reader.getValue().replaceAll("\\n|\\t", "");
if (StringUtils.isBlank(value)) {
map.put(key, unmarshal(reader, unmarshallingContext));
} else {
map.put(key, value);
}
reader.moveUp();
}
return map;
}
@Override
public boolean canConvert(Class clazz) {
return AbstractMap.class.isAssignableFrom(clazz);
}
}
答案 12 :(得分:0)
就我而言,我在Camel ctx中将DBresponse转换为XML。 JDBC执行程序返回具有LinkedCaseInsensitiveMap(单行)的ArrayList(行)。 任务-基于DBResponce创建XML对象。
UPDATE table