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 】 添加BiMap
* 【all 】 cn.hutool.extra.servlet.multipart包迁移到cn.hutool.core.net下
* 【core 】 XmlUtil.mapToXml方法支持集合解析issue#820@Github
### Bug修复
* 【extra 】 修复SpringUtil使用devtools重启报错问题

View File

@ -15,6 +15,31 @@ public class MapBuilder<K, V> implements Serializable{
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
*

View File

@ -17,7 +17,11 @@ import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
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.stream.StreamResult;
import javax.xml.xpath.XPath;
@ -26,12 +30,20 @@ import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.beans.XMLDecoder;
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.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* XML工具类<br>
@ -519,6 +531,16 @@ public class XmlUtil {
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文本中的无效字符
*
@ -956,7 +978,6 @@ public class XmlUtil {
* @since 4.0.9
*/
public static Document mapToXml(Map<?, ?> data, String rootName) {
return mapToXml(data, rootName, null);
}
@ -973,7 +994,7 @@ public class XmlUtil {
final Document doc = createXml();
final Element root = appendChild(doc, rootName, namespace);
mapToXml(doc, root, data);
appendMap(doc, root, data);
return doc;
}
@ -1025,59 +1046,106 @@ public class XmlUtil {
* @since 5.0.4
*/
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);
node.appendChild(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
/**
* 将Map转换为XML格式的字符串
* 追加数据子节点可以是Map集合文本
*
* @param doc {@link Document}
* @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 element 节点
* @param node 当前节点
* @param data Map类型数据
* @since 4.0.8
*/
@SuppressWarnings("rawtypes")
private static void mapToXml(Document doc, Element element, Map<?, ?> data) {
Element filedEle;
Object key;
for (Entry<?, ?> entry : data.entrySet()) {
key = entry.getKey();
if (null == key) {
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()));
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static void appendMap(Document doc, Node node, Map data) {
data.forEach((key, value)->{
if(null != key){
final Element child = appendChild(node, key.toString());
if(null != value){
append(doc, child, value);
}
} 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>
* see: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#JAXP_DocumentBuilderFactory.2C_SAXParserFactory_and_DOM4J

View File

@ -14,12 +14,11 @@ import java.util.Map;
/**
* {@link XmlUtil} 工具类
*
* @author Looly
*
* @author Looly
*/
public class XmlUtilTest {
@Test
public void parseTest() {
String result = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"//
@ -84,7 +83,7 @@ public class XmlUtilTest {
Assert.assertEquals("1490", map.get("remainpoint"));
Assert.assertEquals("885", map.get("taskID"));
Assert.assertEquals("1", map.get("successCounts"));
Assert.assertEquals("subText", ((Map<?, ?>)map.get("newNode")).get("sub"));
Assert.assertEquals("subText", ((Map<?, ?>) map.get("newNode")).get("sub"));
}
@Test
@ -106,17 +105,33 @@ public class XmlUtilTest {
Document doc = XmlUtil.mapToXml(map, "user");
// Console.log(XmlUtil.toStr(doc, false));
Assert.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"//
+ "<user>"//
+ "<name>张三</name>"//
+ "<age>12</age>"//
+ "<game>"//
+ "<昵称>Looly</昵称>"//
+ "<level>14</level>"//
+ "</game>"//
+ "</user>", //
+ "<user>"//
+ "<name>张三</name>"//
+ "<age>12</age>"//
+ "<game>"//
+ "<昵称>Looly</昵称>"//
+ "<level>14</level>"//
+ "</game>"//
+ "</user>", //
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
public void readTest() {
Document doc = XmlUtil.readXML("test.xml");
@ -127,9 +142,9 @@ public class XmlUtilTest {
public void mapToXmlTestWithOmitXmlDeclaration() {
Map<String, Object> map = MapBuilder.create(new LinkedHashMap<String, Object>())
.put("name", "ddatsh")
.build();
String xml=XmlUtil.mapToXmlStr(map,true);
Assert.assertEquals(xml,"<xml><name>ddatsh</name></xml>");
}
.put("name", "ddatsh")
.build();
String xml = XmlUtil.mapToXmlStr(map, true);
Assert.assertEquals("<xml><name>ddatsh</name></xml>", xml);
}
}