XmlUtil.mapToXml support List

This commit is contained in:
Looly 2020-04-09 16:35:44 +08:00
parent 4210ebaa4e
commit ced42166d3
4 changed files with 166 additions and 57 deletions

View File

@ -20,6 +20,7 @@
* 【core 】 添加ValidateObjectInputStream避免对象反序列化漏洞风险 * 【core 】 添加ValidateObjectInputStream避免对象反序列化漏洞风险
* 【core 】 添加BiMap * 【core 】 添加BiMap
* 【all 】 cn.hutool.extra.servlet.multipart包迁移到cn.hutool.core.net下 * 【all 】 cn.hutool.extra.servlet.multipart包迁移到cn.hutool.core.net下
* 【core 】 XmlUtil.mapToXml方法支持集合解析issue#820@Github
### Bug修复 ### Bug修复
* 【extra 】 修复SpringUtil使用devtools重启报错问题 * 【extra 】 修复SpringUtil使用devtools重启报错问题

View File

@ -15,6 +15,31 @@ public class MapBuilder<K, V> implements Serializable{
private Map<K, V> map; private Map<K, V> map;
/**
* 创建Builder默认HashMap实现
*
* @param <K> Key类型
* @param <V> Value类型
* @return MapBuilder
* @since 5.3.0
*/
public static <K, V> MapBuilder<K, V> create() {
return create(false);
}
/**
* 创建Builder
*
* @param <K> Key类型
* @param <V> Value类型
* @param isLinked true创建LinkedHashMapfalse创建HashMap
* @return MapBuilder
* @since 5.3.0
*/
public static <K, V> MapBuilder<K, V> create(boolean isLinked) {
return create(MapUtil.newHashMap(isLinked));
}
/** /**
* 创建Builder * 创建Builder
* *

View File

@ -17,7 +17,11 @@ import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*; import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource; import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath; import javax.xml.xpath.XPath;
@ -26,12 +30,20 @@ import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import java.beans.XMLDecoder; import java.beans.XMLDecoder;
import java.beans.XMLEncoder; import java.beans.XMLEncoder;
import java.io.*; import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
/** /**
* XML工具类<br> * XML工具类<br>
@ -519,6 +531,16 @@ public class XmlUtil {
return (null == doc) ? null : doc.getDocumentElement(); return (null == doc) ? null : doc.getDocumentElement();
} }
/**
* 获取节点所在的Document
* @param node 节点
* @return {@link Document}
* @since 5.3.0
*/
public static Document getOwnerDocument(Node node){
return (node instanceof Document) ? (Document) node : node.getOwnerDocument();
}
/** /**
* 去除XML文本中的无效字符 * 去除XML文本中的无效字符
* *
@ -956,7 +978,6 @@ public class XmlUtil {
* @since 4.0.9 * @since 4.0.9
*/ */
public static Document mapToXml(Map<?, ?> data, String rootName) { public static Document mapToXml(Map<?, ?> data, String rootName) {
return mapToXml(data, rootName, null); return mapToXml(data, rootName, null);
} }
@ -973,7 +994,7 @@ public class XmlUtil {
final Document doc = createXml(); final Document doc = createXml();
final Element root = appendChild(doc, rootName, namespace); final Element root = appendChild(doc, rootName, namespace);
mapToXml(doc, root, data); appendMap(doc, root, data);
return doc; return doc;
} }
@ -1025,59 +1046,106 @@ public class XmlUtil {
* @since 5.0.4 * @since 5.0.4
*/ */
public static Element appendChild(Node node, String tagName, String namespace) { public static Element appendChild(Node node, String tagName, String namespace) {
final Document doc = (node instanceof Document) ? (Document) node : node.getOwnerDocument(); final Document doc = getOwnerDocument(node);
final Element child = (null == namespace) ? doc.createElement(tagName) : doc.createElementNS(namespace, tagName); final Element child = (null == namespace) ? doc.createElement(tagName) : doc.createElementNS(namespace, tagName);
node.appendChild(child); node.appendChild(child);
return child; return child;
} }
/**
* 创建文本子节点
*
* @param node 节点
* @param text 文本
* @return 子节点
* @since 5.3.0
*/
public static Node appendText(Node node, CharSequence text){
return appendText(getOwnerDocument(node), node, text);
}
// ---------------------------------------------------------------------------------------- Private method start // ---------------------------------------------------------------------------------------- Private method start
/** /**
* 将Map转换为XML格式的字符串 * 追加数据子节点可以是Map集合文本
* *
* @param doc {@link Document} * @param doc {@link Document}
* @param element 节点 * @param node 节点
* @param data 数据
*/
@SuppressWarnings("rawtypes")
private static void append(Document doc, Node node, Object data){
if (data instanceof Map) {
// 如果值依旧为map递归继续
appendMap(doc, node, (Map) data);
} else if (data instanceof Iterator) {
// 如果值依旧为map递归继续
appendIterator(doc, node, (Iterator) data);
}else if (data instanceof Iterable) {
// 如果值依旧为map递归继续
appendIterator(doc, node, ((Iterable)data).iterator());
} else {
appendText(doc, node, data.toString());
}
}
/**
* 追加Map数据子节点
*
* @param doc {@link Document}
* @param node 当前节点
* @param data Map类型数据 * @param data Map类型数据
* @since 4.0.8 * @since 4.0.8
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings({"rawtypes", "unchecked"})
private static void mapToXml(Document doc, Element element, Map<?, ?> data) { private static void appendMap(Document doc, Node node, Map data) {
Element filedEle; data.forEach((key, value)->{
Object key; if(null != key){
for (Entry<?, ?> entry : data.entrySet()) { final Element child = appendChild(node, key.toString());
key = entry.getKey(); if(null != value){
if (null == key) { append(doc, child, value);
continue;
}
// key作为标签名无值的节点作为空节点创建
filedEle = doc.createElement(key.toString());
element.appendChild(filedEle);
// value作为标签内的值
final Object value = entry.getValue();
if (null == value) {
continue;
}
if (value instanceof List) {
for (Object listEle : (List) value) {
if (listEle instanceof Map) {
// 如果值依旧为map递归继续
mapToXml(doc, filedEle, (Map<?, ?>) listEle);
} else {
// 创建文本节点
filedEle.appendChild(doc.createTextNode(value.toString()));
} }
} }
} else if (value instanceof Map) { });
// 如果值依旧为map递归继续 }
mapToXml(doc, filedEle, (Map<?, ?>) value);
} else {
filedEle.appendChild(doc.createTextNode(value.toString()));
/**
* 追加集合节点
*
* @param doc {@link Document}
* @param node 节点
* @param data 数据
*/
@SuppressWarnings("rawtypes")
private static void appendIterator(Document doc, Node node, Iterator data){
final Node parentNode = node.getParentNode();
boolean isFirst = true;
Object eleData;
while(data.hasNext()){
eleData = data.next();
if(isFirst){
append(doc, node, eleData);
isFirst = false;
} else{
final Node cloneNode = node.cloneNode(false);
parentNode.appendChild(cloneNode);
append(doc, cloneNode, eleData);
} }
} }
} }
/**
* 追加文本节点
*
* @param doc {@link Document}
* @param node 节点
* @param text 文本内容
* @return 增加的子节点即Text节点
* @since 5.3.0
*/
private static Node appendText(Document doc, Node node, CharSequence text){
return node.appendChild(doc.createTextNode(StrUtil.str(text)));
}
/** /**
* 关闭XXE避免漏洞攻击<br> * 关闭XXE避免漏洞攻击<br>
* see: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#JAXP_DocumentBuilderFactory.2C_SAXParserFactory_and_DOM4J * see: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#JAXP_DocumentBuilderFactory.2C_SAXParserFactory_and_DOM4J

View File

@ -16,7 +16,6 @@ import java.util.Map;
* {@link XmlUtil} 工具类 * {@link XmlUtil} 工具类
* *
* @author Looly * @author Looly
*
*/ */
public class XmlUtilTest { public class XmlUtilTest {
@ -117,6 +116,22 @@ public class XmlUtilTest {
XmlUtil.toStr(doc, false)); XmlUtil.toStr(doc, false));
} }
@Test
public void mapToXmlTest2() {
// 测试List
Map<String, Object> map = MapBuilder.create(new LinkedHashMap<String, Object>())
.put("Town", CollUtil.newArrayList("town1", "town2"))
.build();
Document doc = XmlUtil.mapToXml(map, "City");
Assert.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" +
"<City>" +
"<Town>town1</Town>" +
"<Town>town2</Town>" +
"</City>",
XmlUtil.toStr(doc));
}
@Test @Test
public void readTest() { public void readTest() {
Document doc = XmlUtil.readXML("test.xml"); Document doc = XmlUtil.readXML("test.xml");
@ -130,6 +145,6 @@ public class XmlUtilTest {
.put("name", "ddatsh") .put("name", "ddatsh")
.build(); .build();
String xml = XmlUtil.mapToXmlStr(map, true); String xml = XmlUtil.mapToXmlStr(map, true);
Assert.assertEquals(xml,"<xml><name>ddatsh</name></xml>"); Assert.assertEquals("<xml><name>ddatsh</name></xml>", xml);
} }
} }