mirror of
https://gitee.com/dromara/hutool.git
synced 2025-05-04 20:58:00 +08:00
XmlUtil.mapToXml support List
This commit is contained in:
parent
4210ebaa4e
commit
ced42166d3
@ -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重启报错问题
|
||||||
|
@ -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创建LinkedHashMap,false创建HashMap
|
||||||
|
* @return MapBuilder
|
||||||
|
* @since 5.3.0
|
||||||
|
*/
|
||||||
|
public static <K, V> MapBuilder<K, V> create(boolean isLinked) {
|
||||||
|
return create(MapUtil.newHashMap(isLinked));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建Builder
|
* 创建Builder
|
||||||
*
|
*
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user