mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
add xml
This commit is contained in:
parent
f901c9419d
commit
0760e44c12
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.exception.HutoolException;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link DocumentBuilder} 工具类
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class DocumentBuilderUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 DocumentBuilder
|
||||||
|
*
|
||||||
|
* @param namespaceAware 是否打开命名空间支持
|
||||||
|
* @return DocumentBuilder
|
||||||
|
*/
|
||||||
|
public static DocumentBuilder createDocumentBuilder(final boolean namespaceAware) {
|
||||||
|
final DocumentBuilder builder;
|
||||||
|
try {
|
||||||
|
builder = createDocumentBuilderFactory(namespaceAware).newDocumentBuilder();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new HutoolException(e, "Create xml document error!");
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link DocumentBuilderFactory}
|
||||||
|
* <p>
|
||||||
|
* 默认使用"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"<br>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param namespaceAware 是否打开命名空间支持
|
||||||
|
* @return {@link DocumentBuilderFactory}
|
||||||
|
*/
|
||||||
|
public static DocumentBuilderFactory createDocumentBuilderFactory(final boolean namespaceAware) {
|
||||||
|
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
|
|
||||||
|
// 默认打开NamespaceAware,getElementsByTagNameNS可以使用命名空间
|
||||||
|
factory.setNamespaceAware(namespaceAware);
|
||||||
|
return XXEUtil.disableXXE(factory);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SAXParserFactory} 工具
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class SAXParserFactoryUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sax读取器工厂缓存
|
||||||
|
*/
|
||||||
|
private static volatile SAXParserFactory factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全局{@link SAXParserFactory}<br>
|
||||||
|
* <ul>
|
||||||
|
* <li>默认不验证</li>
|
||||||
|
* <li>默认打开命名空间支持</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return {@link SAXParserFactory}
|
||||||
|
*/
|
||||||
|
public static SAXParserFactory getFactory() {
|
||||||
|
if (null == factory) {
|
||||||
|
synchronized (SAXParserFactoryUtil.class) {
|
||||||
|
if (null == factory) {
|
||||||
|
factory = createFactory(false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link SAXParserFactory}
|
||||||
|
*
|
||||||
|
* @param validating 是否验证
|
||||||
|
* @param namespaceAware 是否打开命名空间支持
|
||||||
|
* @return {@link SAXParserFactory}
|
||||||
|
*/
|
||||||
|
public static SAXParserFactory createFactory(final boolean validating, final boolean namespaceAware) {
|
||||||
|
final SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||||
|
factory.setValidating(validating);
|
||||||
|
factory.setNamespaceAware(namespaceAware);
|
||||||
|
|
||||||
|
return XXEUtil.disableXXE(factory);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.map.BiMap;
|
||||||
|
import org.w3c.dom.NamedNodeMap;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
import javax.xml.XMLConstants;
|
||||||
|
import javax.xml.namespace.NamespaceContext;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局命名空间上下文<br>
|
||||||
|
* 见:https://www.ibm.com/developerworks/cn/xml/x-nmspccontext/
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
public class UniversalNamespaceCache implements NamespaceContext {
|
||||||
|
private static final String DEFAULT_NS = "DEFAULT";
|
||||||
|
private final BiMap<String, String> prefixUri = new BiMap<>(new HashMap<>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor parses the document and stores all namespaces it can
|
||||||
|
* find. If toplevelOnly is true, only namespaces in the root are used.
|
||||||
|
*
|
||||||
|
* @param node source Node
|
||||||
|
* @param toplevelOnly restriction of the search to enhance performance
|
||||||
|
*/
|
||||||
|
public UniversalNamespaceCache(final Node node, final boolean toplevelOnly) {
|
||||||
|
examineNode(node.getFirstChild(), toplevelOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single node is read, the namespace attributes are extracted and stored.
|
||||||
|
*
|
||||||
|
* @param node to examine
|
||||||
|
* @param attributesOnly, if true no recursion happens
|
||||||
|
*/
|
||||||
|
private void examineNode(final Node node, final boolean attributesOnly) {
|
||||||
|
final NamedNodeMap attributes = node.getAttributes();
|
||||||
|
if (null != attributes) {
|
||||||
|
final int length = attributes.getLength();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
final Node attribute = attributes.item(i);
|
||||||
|
storeAttribute(attribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attributesOnly) {
|
||||||
|
final NodeList childNodes = node.getChildNodes();
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
if (null != childNodes) {
|
||||||
|
Node item;
|
||||||
|
final int childLength = childNodes.getLength();
|
||||||
|
for (int i = 0; i < childLength; i++) {
|
||||||
|
item = childNodes.item(i);
|
||||||
|
if (item.getNodeType() == Node.ELEMENT_NODE)
|
||||||
|
examineNode(item, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method looks at an attribute and stores it, if it is a namespace
|
||||||
|
* attribute.
|
||||||
|
*
|
||||||
|
* @param attribute to examine
|
||||||
|
*/
|
||||||
|
private void storeAttribute(final Node attribute) {
|
||||||
|
if (null == attribute) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// examine the attributes in namespace xmlns
|
||||||
|
if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(attribute.getNamespaceURI())) {
|
||||||
|
// Default namespace xmlns="uri goes here"
|
||||||
|
if (XMLConstants.XMLNS_ATTRIBUTE.equals(attribute.getNodeName())) {
|
||||||
|
prefixUri.put(DEFAULT_NS, attribute.getNodeValue());
|
||||||
|
} else {
|
||||||
|
// The defined prefixes are stored here
|
||||||
|
prefixUri.put(attribute.getLocalName(), attribute.getNodeValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by XPath. It returns the default namespace, if the
|
||||||
|
* prefix is null or "".
|
||||||
|
*
|
||||||
|
* @param prefix to search for
|
||||||
|
* @return uri
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getNamespaceURI(final String prefix) {
|
||||||
|
if (prefix == null || XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
|
||||||
|
return prefixUri.get(DEFAULT_NS);
|
||||||
|
} else {
|
||||||
|
return prefixUri.get(prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is not needed in this context, but can be implemented in a
|
||||||
|
* similar way.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getPrefix(final String namespaceURI) {
|
||||||
|
return prefixUri.getInverse().get(namespaceURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<String> getPrefixes(final String namespaceURI) {
|
||||||
|
// Not implemented
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
132
hutool-core/src/main/java/org/dromara/hutool/core/xml/XPathUtil.java
Executable file
132
hutool-core/src/main/java/org/dromara/hutool/core/xml/XPathUtil.java
Executable file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.exception.HutoolException;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
import javax.xml.namespace.NamespaceContext;
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
import javax.xml.xpath.XPath;
|
||||||
|
import javax.xml.xpath.XPathConstants;
|
||||||
|
import javax.xml.xpath.XPathExpressionException;
|
||||||
|
import javax.xml.xpath.XPathFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link XPath}相关工具类
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class XPathUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建XPath<br>
|
||||||
|
* Xpath相关文章:<a href="https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html">https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html</a>
|
||||||
|
*
|
||||||
|
* @return {@link XPath}
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public static XPath createXPath() {
|
||||||
|
return XPathFactory.newInstance().newXPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过XPath方式读取XML节点等信息<br>
|
||||||
|
* Xpath相关文章:<a href="https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html">https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html</a>
|
||||||
|
*
|
||||||
|
* @param expression XPath表达式
|
||||||
|
* @param source 资源,可以是Docunent、Node节点等
|
||||||
|
* @return 匹配返回类型的值
|
||||||
|
* @since 4.0.9
|
||||||
|
*/
|
||||||
|
public static Element getElementByXPath(final String expression, final Object source) {
|
||||||
|
return (Element) getNodeByXPath(expression, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过XPath方式读取XML的NodeList<br>
|
||||||
|
* Xpath相关文章:<a href="https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html">https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html</a>
|
||||||
|
*
|
||||||
|
* @param expression XPath表达式
|
||||||
|
* @param source 资源,可以是Docunent、Node节点等
|
||||||
|
* @return NodeList
|
||||||
|
* @since 4.0.9
|
||||||
|
*/
|
||||||
|
public static NodeList getNodeListByXPath(final String expression, final Object source) {
|
||||||
|
return (NodeList) getByXPath(expression, source, XPathConstants.NODESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过XPath方式读取XML节点等信息<br>
|
||||||
|
* Xpath相关文章:<a href="https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html">https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html</a>
|
||||||
|
*
|
||||||
|
* @param expression XPath表达式
|
||||||
|
* @param source 资源,可以是Docunent、Node节点等
|
||||||
|
* @return 匹配返回类型的值
|
||||||
|
* @since 4.0.9
|
||||||
|
*/
|
||||||
|
public static Node getNodeByXPath(final String expression, final Object source) {
|
||||||
|
return (Node) getByXPath(expression, source, XPathConstants.NODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过XPath方式读取XML节点等信息<br>
|
||||||
|
* Xpath相关文章:<a href="https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html">https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html</a>
|
||||||
|
*
|
||||||
|
* @param expression XPath表达式
|
||||||
|
* @param source 资源,可以是Docunent、Node节点等
|
||||||
|
* @param returnType 返回类型,{@link javax.xml.xpath.XPathConstants}
|
||||||
|
* @return 匹配返回类型的值
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public static Object getByXPath(final String expression, final Object source, final QName returnType) {
|
||||||
|
NamespaceContext nsContext = null;
|
||||||
|
if (source instanceof Node) {
|
||||||
|
nsContext = new UniversalNamespaceCache((Node) source, false);
|
||||||
|
}
|
||||||
|
return getByXPath(expression, source, returnType, nsContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过XPath方式读取XML节点等信息<br>
|
||||||
|
* Xpath相关文章:<br>
|
||||||
|
* <a href="https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html">https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html</a><br>
|
||||||
|
* <a href="https://www.ibm.com/developerworks/cn/xml/x-nmspccontext/">https://www.ibm.com/developerworks/cn/xml/x-nmspccontext/</a>
|
||||||
|
*
|
||||||
|
* @param expression XPath表达式
|
||||||
|
* @param source 资源,可以是Docunent、Node节点等
|
||||||
|
* @param returnType 返回类型,{@link javax.xml.xpath.XPathConstants}
|
||||||
|
* @param nsContext {@link NamespaceContext}
|
||||||
|
* @return 匹配返回类型的值
|
||||||
|
* @since 5.3.1
|
||||||
|
*/
|
||||||
|
public static Object getByXPath(final String expression, final Object source, final QName returnType, final NamespaceContext nsContext) {
|
||||||
|
final XPath xPath = createXPath();
|
||||||
|
if (null != nsContext) {
|
||||||
|
xPath.setNamespaceContext(nsContext);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (source instanceof InputSource) {
|
||||||
|
return xPath.evaluate(expression, (InputSource) source, returnType);
|
||||||
|
} else {
|
||||||
|
return xPath.evaluate(expression, source, returnType);
|
||||||
|
}
|
||||||
|
} catch (final XPathExpressionException e) {
|
||||||
|
throw new HutoolException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
172
hutool-core/src/main/java/org/dromara/hutool/core/xml/XXEUtil.java
Executable file
172
hutool-core/src/main/java/org/dromara/hutool/core/xml/XXEUtil.java
Executable file
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.exception.HutoolException;
|
||||||
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
|
||||||
|
import javax.xml.XMLConstants;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
import javax.xml.transform.TransformerConfigurationException;
|
||||||
|
import javax.xml.transform.TransformerFactory;
|
||||||
|
import javax.xml.transform.sax.SAXTransformerFactory;
|
||||||
|
import javax.xml.validation.SchemaFactory;
|
||||||
|
import javax.xml.validation.Validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XXE漏洞修复相关工具类<br>
|
||||||
|
* 参考:https://blog.spoock.com/2018/10/23/java-xxe/
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class XXEUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭XXE,避免漏洞攻击<br>
|
||||||
|
* see: <a href="https://www.owasp.org/index.php/XML_External_Entity_">https://www.owasp.org/index.php/XML_External_Entity_</a>(XXE)_Prevention_Cheat_Sheet#JAXP_DocumentBuilderFactory.2C_SAXParserFactory_and_DOM4J
|
||||||
|
*
|
||||||
|
* @param factory DocumentBuilderFactory
|
||||||
|
* @return DocumentBuilderFactory
|
||||||
|
*/
|
||||||
|
public static DocumentBuilderFactory disableXXE(final DocumentBuilderFactory factory) {
|
||||||
|
try {
|
||||||
|
// This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented
|
||||||
|
// Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
|
||||||
|
factory.setFeature(XmlFeatures.DISALLOW_DOCTYPE_DECL, true);
|
||||||
|
// If you can't completely disable DTDs, then at least do the following:
|
||||||
|
// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
|
||||||
|
// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
|
||||||
|
// JDK7+ - http://xml.org/sax/features/external-general-entities
|
||||||
|
factory.setFeature(XmlFeatures.EXTERNAL_GENERAL_ENTITIES, false);
|
||||||
|
// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
|
||||||
|
// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
|
||||||
|
// JDK7+ - http://xml.org/sax/features/external-parameter-entities
|
||||||
|
factory.setFeature(XmlFeatures.EXTERNAL_PARAMETER_ENTITIES, false);
|
||||||
|
// Disable external DTDs as well
|
||||||
|
factory.setFeature(XmlFeatures.LOAD_EXTERNAL_DTD, false);
|
||||||
|
} catch (final ParserConfigurationException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
|
||||||
|
factory.setXIncludeAware(false);
|
||||||
|
factory.setExpandEntityReferences(false);
|
||||||
|
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭XEE避免漏洞攻击
|
||||||
|
*
|
||||||
|
* @param factory {@link SAXParserFactory}
|
||||||
|
* @return {@link SAXParserFactory}
|
||||||
|
*/
|
||||||
|
public static SAXParserFactory disableXXE(final SAXParserFactory factory) {
|
||||||
|
try {
|
||||||
|
factory.setFeature(XmlFeatures.DISALLOW_DOCTYPE_DECL, true);
|
||||||
|
factory.setFeature(XmlFeatures.EXTERNAL_GENERAL_ENTITIES, false);
|
||||||
|
factory.setFeature(XmlFeatures.EXTERNAL_PARAMETER_ENTITIES, false);
|
||||||
|
factory.setFeature(XmlFeatures.LOAD_EXTERNAL_DTD, false);
|
||||||
|
} catch (final Exception ignore) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
factory.setXIncludeAware(false);
|
||||||
|
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭XEE避免漏洞攻击
|
||||||
|
*
|
||||||
|
* @param reader {@link XMLReader}
|
||||||
|
* @return {@link XMLReader}
|
||||||
|
*/
|
||||||
|
public static XMLReader disableXXE(final XMLReader reader) {
|
||||||
|
try {
|
||||||
|
reader.setFeature(XmlFeatures.DISALLOW_DOCTYPE_DECL, true);
|
||||||
|
reader.setFeature(XmlFeatures.EXTERNAL_GENERAL_ENTITIES, false);
|
||||||
|
reader.setFeature(XmlFeatures.EXTERNAL_PARAMETER_ENTITIES, false);
|
||||||
|
reader.setFeature(XmlFeatures.LOAD_EXTERNAL_DTD, false);
|
||||||
|
} catch (final Exception ignore) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭XEE避免漏洞攻击
|
||||||
|
*
|
||||||
|
* @param factory {@link TransformerFactory }
|
||||||
|
* @return {@link TransformerFactory }
|
||||||
|
*/
|
||||||
|
public static TransformerFactory disableXXE(final TransformerFactory factory) {
|
||||||
|
try {
|
||||||
|
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||||
|
} catch (final TransformerConfigurationException e) {
|
||||||
|
throw new HutoolException(e);
|
||||||
|
}
|
||||||
|
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, StrUtil.EMPTY);
|
||||||
|
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, StrUtil.EMPTY);
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭XEE避免漏洞攻击
|
||||||
|
*
|
||||||
|
* @param validator {@link Validator }
|
||||||
|
* @return {@link Validator }
|
||||||
|
*/
|
||||||
|
public static Validator disableXXE(final Validator validator) {
|
||||||
|
try {
|
||||||
|
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, StrUtil.EMPTY);
|
||||||
|
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, StrUtil.EMPTY);
|
||||||
|
} catch (final Exception ignore) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭XEE避免漏洞攻击
|
||||||
|
*
|
||||||
|
* @param factory {@link SAXTransformerFactory}
|
||||||
|
* @return {@link SAXTransformerFactory}
|
||||||
|
*/
|
||||||
|
public static SAXTransformerFactory disableXXE(final SAXTransformerFactory factory) {
|
||||||
|
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, StrUtil.EMPTY);
|
||||||
|
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, StrUtil.EMPTY);
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭XEE避免漏洞攻击
|
||||||
|
*
|
||||||
|
* @param factory {@link SchemaFactory}
|
||||||
|
* @return {@link SchemaFactory}
|
||||||
|
*/
|
||||||
|
public static SchemaFactory disableXXE(final SchemaFactory factory) {
|
||||||
|
try {
|
||||||
|
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, StrUtil.EMPTY);
|
||||||
|
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, StrUtil.EMPTY);
|
||||||
|
} catch (final Exception ignore) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
}
|
66
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlConstants.java
Executable file
66
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlConstants.java
Executable file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML相关常量
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class XmlConstants {
|
||||||
|
/**
|
||||||
|
* 字符串常量:XML 不间断空格转义 {@code " " -> " "}
|
||||||
|
*/
|
||||||
|
public static final String NBSP = " ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串常量:XML And 符转义 {@code "&" -> "&"}
|
||||||
|
*/
|
||||||
|
public static final String AMP = "&";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串常量:XML 双引号转义 {@code """ -> "\""}
|
||||||
|
*/
|
||||||
|
public static final String QUOTE = """;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串常量:XML 单引号转义 {@code "&apos" -> "'"}
|
||||||
|
*/
|
||||||
|
public static final String APOS = "'";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串常量:XML 小于号转义 {@code "<" -> "<"}
|
||||||
|
*/
|
||||||
|
public static final String LT = "<";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串常量:XML 大于号转义 {@code ">" -> ">"}
|
||||||
|
*/
|
||||||
|
public static final String GT = ">";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在XML中无效的字符 正则
|
||||||
|
*/
|
||||||
|
public static final Pattern INVALID_PATTERN = Pattern.compile("[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]");
|
||||||
|
/**
|
||||||
|
* 在XML中注释的内容 正则
|
||||||
|
*/
|
||||||
|
public static final Pattern COMMENT_PATTERN = Pattern.compile("(?s)<!--.+?-->");
|
||||||
|
/**
|
||||||
|
* XML格式化输出默认缩进量
|
||||||
|
*/
|
||||||
|
public static final int INDENT_DEFAULT = 2;
|
||||||
|
}
|
41
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlFeatures.java
Executable file
41
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlFeatures.java
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XXE安全相关参数<br>
|
||||||
|
* 见:https://blog.spoock.com/2018/10/23/java-xxe/
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class XmlFeatures {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁用xml中的inline DOCTYPE 声明,即禁用DTD<br>
|
||||||
|
* 不允许将外部实体包含在传入的 XML 文档中,从而防止XML实体注入(XML External Entities 攻击,利用能够在处理时动态构建文档的 XML 功能,注入外部实体)
|
||||||
|
*/
|
||||||
|
public static final String DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl";
|
||||||
|
/**
|
||||||
|
* 忽略外部DTD
|
||||||
|
*/
|
||||||
|
public static final String LOAD_EXTERNAL_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
|
||||||
|
/**
|
||||||
|
* 不包括外部一般实体
|
||||||
|
*/
|
||||||
|
public static final String EXTERNAL_GENERAL_ENTITIES = "http://xml.org/sax/features/external-general-entities";
|
||||||
|
/**
|
||||||
|
* 不包含外部参数实体或外部DTD子集。
|
||||||
|
*/
|
||||||
|
public static final String EXTERNAL_PARAMETER_ENTITIES = "http://xml.org/sax/features/external-parameter-entities";
|
||||||
|
}
|
147
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlMapper.java
Executable file
147
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlMapper.java
Executable file
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.bean.BeanUtil;
|
||||||
|
import org.dromara.hutool.core.collection.CollUtil;
|
||||||
|
import org.dromara.hutool.core.collection.ListUtil;
|
||||||
|
import org.dromara.hutool.core.map.MapUtil;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML转换器,用于转换Map或Bean等
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class XmlMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建XmlMapper
|
||||||
|
*
|
||||||
|
* @param node {@link Node}XML节点
|
||||||
|
* @return XmlMapper
|
||||||
|
*/
|
||||||
|
public static XmlMapper of(final Node node) {
|
||||||
|
return new XmlMapper(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Node node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param node {@link Node}XML节点
|
||||||
|
*/
|
||||||
|
public XmlMapper(final Node node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML转Java Bean<br>
|
||||||
|
* 如果XML根节点只有一个,且节点名和Bean的名称一致,则直接转换子节点
|
||||||
|
*
|
||||||
|
* @param <T> bean类型
|
||||||
|
* @param bean bean类
|
||||||
|
* @return bean
|
||||||
|
* @since 5.2.4
|
||||||
|
*/
|
||||||
|
public <T> T toBean(final Class<T> bean) {
|
||||||
|
final Map<String, Object> map = toMap();
|
||||||
|
if (null != map && map.size() == 1) {
|
||||||
|
final String nodeName = CollUtil.getFirst(map.keySet());
|
||||||
|
if (bean.getSimpleName().equalsIgnoreCase(nodeName)) {
|
||||||
|
// 只有key和bean的名称匹配时才做单一对象转换
|
||||||
|
return BeanUtil.toBean(CollUtil.get(map.values(), 0), bean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BeanUtil.toBean(map, bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML节点转Map
|
||||||
|
*
|
||||||
|
* @return map
|
||||||
|
*/
|
||||||
|
public Map<String, Object> toMap() {
|
||||||
|
return toMap(new LinkedHashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML节点转Map
|
||||||
|
*
|
||||||
|
* @param result 结果Map
|
||||||
|
* @return map
|
||||||
|
*/
|
||||||
|
public Map<String, Object> toMap(final Map<String, Object> result) {
|
||||||
|
return toMap(this.node, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML节点转Map
|
||||||
|
*
|
||||||
|
* @param result 结果Map
|
||||||
|
* @return map
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Map<String, Object> toMap(final Node node, Map<String, Object> result) {
|
||||||
|
if (null == result) {
|
||||||
|
result = new HashMap<>();
|
||||||
|
}
|
||||||
|
final NodeList nodeList = node.getChildNodes();
|
||||||
|
final int length = nodeList.getLength();
|
||||||
|
Node childNode;
|
||||||
|
Element childEle;
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
childNode = nodeList.item(i);
|
||||||
|
if (!XmlUtil.isElement(childNode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
childEle = (Element) childNode;
|
||||||
|
final Object value = result.get(childEle.getNodeName());
|
||||||
|
final Object newValue;
|
||||||
|
if (childEle.hasChildNodes()) {
|
||||||
|
// 子节点继续递归遍历
|
||||||
|
final Map<String, Object> map = toMap(childEle, new LinkedHashMap<>());
|
||||||
|
if (MapUtil.isNotEmpty(map)) {
|
||||||
|
newValue = map;
|
||||||
|
} else {
|
||||||
|
newValue = childEle.getTextContent();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newValue = childEle.getTextContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != newValue) {
|
||||||
|
if (null != value) {
|
||||||
|
if (value instanceof List) {
|
||||||
|
((List<Object>) value).add(newValue);
|
||||||
|
} else {
|
||||||
|
result.put(childEle.getNodeName(), ListUtil.of(value, newValue));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.put(childEle.getNodeName(), newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
96
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlSaxReader.java
Executable file
96
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlSaxReader.java
Executable file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.exception.HutoolException;
|
||||||
|
import org.dromara.hutool.core.io.IORuntimeException;
|
||||||
|
import org.xml.sax.ContentHandler;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.parsers.SAXParser;
|
||||||
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML SAX方式读取器
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class XmlSaxReader {
|
||||||
|
|
||||||
|
private final SAXParserFactory factory;
|
||||||
|
private final InputSource source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建XmlSaxReader,使用全局{@link SAXParserFactory}
|
||||||
|
*
|
||||||
|
* @param source XML源,可以是文件、流、路径等
|
||||||
|
* @return XmlSaxReader
|
||||||
|
*/
|
||||||
|
public static XmlSaxReader of(final InputSource source) {
|
||||||
|
return of(SAXParserFactoryUtil.getFactory(), source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建XmlSaxReader
|
||||||
|
*
|
||||||
|
* @param factory {@link SAXParserFactory}
|
||||||
|
* @param source XML源,可以是文件、流、路径等
|
||||||
|
* @return XmlSaxReader
|
||||||
|
*/
|
||||||
|
public static XmlSaxReader of(final SAXParserFactory factory, final InputSource source) {
|
||||||
|
return new XmlSaxReader(factory, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param factory {@link SAXParserFactory}
|
||||||
|
* @param source XML源,可以是文件、流、路径等
|
||||||
|
*/
|
||||||
|
public XmlSaxReader(final SAXParserFactory factory, final InputSource source) {
|
||||||
|
this.factory = factory;
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取内容
|
||||||
|
*
|
||||||
|
* @param contentHandler XML流处理器,用于按照Element处理xml
|
||||||
|
*/
|
||||||
|
public void read(final ContentHandler contentHandler) {
|
||||||
|
final SAXParser parse;
|
||||||
|
final XMLReader reader;
|
||||||
|
try {
|
||||||
|
parse = factory.newSAXParser();
|
||||||
|
if (contentHandler instanceof DefaultHandler) {
|
||||||
|
parse.parse(source, (DefaultHandler) contentHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 得到解读器
|
||||||
|
reader = XXEUtil.disableXXE(parse.getXMLReader());
|
||||||
|
reader.setContentHandler(contentHandler);
|
||||||
|
reader.parse(source);
|
||||||
|
} catch (final ParserConfigurationException | SAXException e) {
|
||||||
|
throw new HutoolException(e);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
173
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlWriter.java
Executable file
173
hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlWriter.java
Executable file
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.exception.HutoolException;
|
||||||
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
import org.dromara.hutool.core.util.CharsetUtil;
|
||||||
|
import org.dromara.hutool.core.util.ObjUtil;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
|
import javax.xml.transform.*;
|
||||||
|
import javax.xml.transform.dom.DOMSource;
|
||||||
|
import javax.xml.transform.stream.StreamResult;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML生成器
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class XmlWriter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建XmlWriter
|
||||||
|
*
|
||||||
|
* @param node {@link Node} XML文档节点或文档本身
|
||||||
|
* @return XmlWriter
|
||||||
|
*/
|
||||||
|
public static XmlWriter of(final Node node) {
|
||||||
|
return of(new DOMSource(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建XmlWriter
|
||||||
|
*
|
||||||
|
* @param source XML数据源
|
||||||
|
* @return XmlWriter
|
||||||
|
*/
|
||||||
|
public static XmlWriter of(final Source source) {
|
||||||
|
return new XmlWriter(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Source source;
|
||||||
|
|
||||||
|
private Charset charset = CharsetUtil.UTF_8;
|
||||||
|
private int indent;
|
||||||
|
private boolean omitXmlDeclaration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param source XML数据源
|
||||||
|
*/
|
||||||
|
public XmlWriter(final Source source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置编码
|
||||||
|
*
|
||||||
|
* @param charset 编码,null跳过
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public XmlWriter setCharset(final Charset charset) {
|
||||||
|
if (null != charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置缩进
|
||||||
|
*
|
||||||
|
* @param indent 缩进
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public XmlWriter setIndent(final int indent) {
|
||||||
|
this.indent = indent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否输出 xml Declaration
|
||||||
|
*
|
||||||
|
* @param omitXmlDeclaration 是否输出 xml Declaration
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public XmlWriter setOmitXmlDeclaration(final boolean omitXmlDeclaration) {
|
||||||
|
this.omitXmlDeclaration = omitXmlDeclaration;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得XML字符串
|
||||||
|
*
|
||||||
|
* @return XML字符串
|
||||||
|
*/
|
||||||
|
public String getStr(){
|
||||||
|
final StringWriter writer = StrUtil.getWriter();
|
||||||
|
write(writer);
|
||||||
|
return writer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将XML文档写出
|
||||||
|
*
|
||||||
|
* @param file 目标
|
||||||
|
*/
|
||||||
|
public void write(final File file) {
|
||||||
|
write(new StreamResult(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将XML文档写出
|
||||||
|
*
|
||||||
|
* @param writer 目标
|
||||||
|
*/
|
||||||
|
public void write(final Writer writer) {
|
||||||
|
write(new StreamResult(writer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将XML文档写出
|
||||||
|
*
|
||||||
|
* @param out 目标
|
||||||
|
*/
|
||||||
|
public void write(final OutputStream out) {
|
||||||
|
write(new StreamResult(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将XML文档写出<br>
|
||||||
|
* 格式化输出逻辑参考:https://stackoverflow.com/questions/139076/how-to-pretty-print-xml-from-java
|
||||||
|
*
|
||||||
|
* @param result 目标
|
||||||
|
*/
|
||||||
|
public void write(final Result result) {
|
||||||
|
final TransformerFactory factory = XXEUtil.disableXXE(TransformerFactory.newInstance());
|
||||||
|
try {
|
||||||
|
final Transformer xformer = factory.newTransformer();
|
||||||
|
if (indent > 0) {
|
||||||
|
xformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||||
|
//fix issue#1232@Github
|
||||||
|
xformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes");
|
||||||
|
xformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indent));
|
||||||
|
}
|
||||||
|
if (ObjUtil.isNotNull(this.charset)) {
|
||||||
|
xformer.setOutputProperty(OutputKeys.ENCODING, charset.name());
|
||||||
|
}
|
||||||
|
if (omitXmlDeclaration) {
|
||||||
|
xformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||||
|
}
|
||||||
|
xformer.transform(source, result);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new HutoolException(e, "Trans xml document to string error!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package org.dromara.hutool.core.text.escape;
|
package org.dromara.hutool.core.text.escape;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.lang.Console;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -66,4 +67,11 @@ public class EscapeUtilTest {
|
|||||||
final String s = EscapeUtil.unescapeHtml4(str);
|
final String s = EscapeUtil.unescapeHtml4(str);
|
||||||
Assertions.assertEquals("'some text with single quotes'", s);
|
Assertions.assertEquals("'some text with single quotes'", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void escapeXmlTest(){
|
||||||
|
final String a = "<>";
|
||||||
|
final String escape = EscapeUtil.escape(a);
|
||||||
|
Console.log(escape);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,17 +10,18 @@
|
|||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.dromara.hutool.core.util;
|
package org.dromara.hutool.core.xml;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.dromara.hutool.core.bean.BeanUtil;
|
import org.dromara.hutool.core.bean.BeanUtil;
|
||||||
import org.dromara.hutool.core.collection.ListUtil;
|
import org.dromara.hutool.core.collection.ListUtil;
|
||||||
import org.dromara.hutool.core.collection.set.SetUtil;
|
import org.dromara.hutool.core.collection.set.SetUtil;
|
||||||
|
import org.dromara.hutool.core.io.file.FileUtil;
|
||||||
import org.dromara.hutool.core.io.resource.ResourceUtil;
|
import org.dromara.hutool.core.io.resource.ResourceUtil;
|
||||||
import org.dromara.hutool.core.lang.Console;
|
import org.dromara.hutool.core.lang.Console;
|
||||||
import org.dromara.hutool.core.map.MapBuilder;
|
import org.dromara.hutool.core.map.MapBuilder;
|
||||||
import org.dromara.hutool.core.map.MapUtil;
|
import org.dromara.hutool.core.map.MapUtil;
|
||||||
import org.dromara.hutool.core.xml.XmlUtil;
|
import org.dromara.hutool.core.util.CharsetUtil;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -46,13 +47,13 @@ public class XmlUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void parseTest() {
|
public void parseTest() {
|
||||||
final String result = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"//
|
final String result = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"//
|
||||||
+ "<returnsms>"//
|
+ "<returnsms>"//
|
||||||
+ "<returnstatus>Success</returnstatus>"//
|
+ "<returnstatus>Success</returnstatus>"//
|
||||||
+ "<message>ok</message>"//
|
+ "<message>ok</message>"//
|
||||||
+ "<remainpoint>1490</remainpoint>"//
|
+ "<remainpoint>1490</remainpoint>"//
|
||||||
+ "<taskID>885</taskID>"//
|
+ "<taskID>885</taskID>"//
|
||||||
+ "<successCounts>1</successCounts>"//
|
+ "<successCounts>1</successCounts>"//
|
||||||
+ "</returnsms>";
|
+ "</returnsms>";
|
||||||
final Document docResult = XmlUtil.parseXml(result);
|
final Document docResult = XmlUtil.parseXml(result);
|
||||||
final String elementText = XmlUtil.elementText(docResult.getDocumentElement(), "returnstatus");
|
final String elementText = XmlUtil.elementText(docResult.getDocumentElement(), "returnstatus");
|
||||||
Assertions.assertEquals("Success", elementText);
|
Assertions.assertEquals("Success", elementText);
|
||||||
@ -62,29 +63,29 @@ public class XmlUtilTest {
|
|||||||
@Disabled
|
@Disabled
|
||||||
public void writeTest() {
|
public void writeTest() {
|
||||||
final String result = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"//
|
final String result = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"//
|
||||||
+ "<returnsms>"//
|
+ "<returnsms>"//
|
||||||
+ "<returnstatus>Success(成功)</returnstatus>"//
|
+ "<returnstatus>Success(成功)</returnstatus>"//
|
||||||
+ "<message>ok</message>"//
|
+ "<message>ok</message>"//
|
||||||
+ "<remainpoint>1490</remainpoint>"//
|
+ "<remainpoint>1490</remainpoint>"//
|
||||||
+ "<taskID>885</taskID>"//
|
+ "<taskID>885</taskID>"//
|
||||||
+ "<successCounts>1</successCounts>"//
|
+ "<successCounts>1</successCounts>"//
|
||||||
+ "</returnsms>";
|
+ "</returnsms>";
|
||||||
final Document docResult = XmlUtil.parseXml(result);
|
final Document docResult = XmlUtil.parseXml(result);
|
||||||
XmlUtil.toFile(docResult, "e:/aaa.xml", "utf-8");
|
XmlUtil.write(docResult, FileUtil.file("d:/test/aaa.xml"), CharsetUtil.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void xpathTest() {
|
public void xpathTest() {
|
||||||
final String result = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"//
|
final String result = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"//
|
||||||
+ "<returnsms>"//
|
+ "<returnsms>"//
|
||||||
+ "<returnstatus>Success(成功)</returnstatus>"//
|
+ "<returnstatus>Success(成功)</returnstatus>"//
|
||||||
+ "<message>ok</message>"//
|
+ "<message>ok</message>"//
|
||||||
+ "<remainpoint>1490</remainpoint>"//
|
+ "<remainpoint>1490</remainpoint>"//
|
||||||
+ "<taskID>885</taskID>"//
|
+ "<taskID>885</taskID>"//
|
||||||
+ "<successCounts>1</successCounts>"//
|
+ "<successCounts>1</successCounts>"//
|
||||||
+ "</returnsms>";
|
+ "</returnsms>";
|
||||||
final Document docResult = XmlUtil.parseXml(result);
|
final Document docResult = XmlUtil.parseXml(result);
|
||||||
final Object value = XmlUtil.getByXPath("//returnsms/message", docResult, XPathConstants.STRING);
|
final Object value = XPathUtil.getByXPath("//returnsms/message", docResult, XPathConstants.STRING);
|
||||||
Assertions.assertEquals("ok", value);
|
Assertions.assertEquals("ok", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,21 +93,21 @@ public class XmlUtilTest {
|
|||||||
public void xpathTest2() {
|
public void xpathTest2() {
|
||||||
final String result = ResourceUtil.readUtf8Str("test.xml");
|
final String result = ResourceUtil.readUtf8Str("test.xml");
|
||||||
final Document docResult = XmlUtil.parseXml(result);
|
final Document docResult = XmlUtil.parseXml(result);
|
||||||
final Object value = XmlUtil.getByXPath("//returnsms/message", docResult, XPathConstants.STRING);
|
final Object value = XPathUtil.getByXPath("//returnsms/message", docResult, XPathConstants.STRING);
|
||||||
Assertions.assertEquals("ok", value);
|
Assertions.assertEquals("ok", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void xmlToMapTest() {
|
public void xmlToMapTest() {
|
||||||
final String xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"//
|
final String xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"//
|
||||||
+ "<returnsms>"//
|
+ "<returnsms>"//
|
||||||
+ "<returnstatus>Success</returnstatus>"//
|
+ "<returnstatus>Success</returnstatus>"//
|
||||||
+ "<message>ok</message>"//
|
+ "<message>ok</message>"//
|
||||||
+ "<remainpoint>1490</remainpoint>"//
|
+ "<remainpoint>1490</remainpoint>"//
|
||||||
+ "<taskID>885</taskID>"//
|
+ "<taskID>885</taskID>"//
|
||||||
+ "<successCounts>1</successCounts>"//
|
+ "<successCounts>1</successCounts>"//
|
||||||
+ "<newNode><sub>subText</sub></newNode>"//
|
+ "<newNode><sub>subText</sub></newNode>"//
|
||||||
+ "</returnsms>";
|
+ "</returnsms>";
|
||||||
final Map<String, Object> map = XmlUtil.xmlToMap(xml);
|
final Map<String, Object> map = XmlUtil.xmlToMap(xml);
|
||||||
|
|
||||||
Assertions.assertEquals(6, map.size());
|
Assertions.assertEquals(6, map.size());
|
||||||
@ -130,51 +131,51 @@ public class XmlUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void mapToXmlTest() {
|
public void mapToXmlTest() {
|
||||||
final Map<String, Object> map = MapBuilder.of(new LinkedHashMap<String, Object>())//
|
final Map<String, Object> map = MapBuilder.of(new LinkedHashMap<String, Object>())//
|
||||||
.put("name", "张三")//
|
.put("name", "张三")//
|
||||||
.put("age", 12)//
|
.put("age", 12)//
|
||||||
.put("game", MapUtil.builder(new LinkedHashMap<String, Object>()).put("昵称", "Looly").put("level", 14).build())//
|
.put("game", MapUtil.builder(new LinkedHashMap<String, Object>()).put("昵称", "Looly").put("level", 14).build())//
|
||||||
.build();
|
.build();
|
||||||
final Document doc = XmlUtil.mapToXml(map, "user");
|
final Document doc = XmlUtil.mapToXml(map, "user");
|
||||||
// Console.log(XmlUtil.toStr(doc, false));
|
// Console.log(XmlUtil.toStr(doc, false));
|
||||||
Assertions.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"//
|
Assertions.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"//
|
||||||
+ "<user>"//
|
+ "<user>"//
|
||||||
+ "<name>张三</name>"//
|
+ "<name>张三</name>"//
|
||||||
+ "<age>12</age>"//
|
+ "<age>12</age>"//
|
||||||
+ "<game>"//
|
+ "<game>"//
|
||||||
+ "<昵称>Looly</昵称>"//
|
+ "<昵称>Looly</昵称>"//
|
||||||
+ "<level>14</level>"//
|
+ "<level>14</level>"//
|
||||||
+ "</game>"//
|
+ "</game>"//
|
||||||
+ "</user>", //
|
+ "</user>", //
|
||||||
XmlUtil.toStr(doc, false));
|
XmlUtil.toStr(doc, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mapToXmlTest2() {
|
public void mapToXmlTest2() {
|
||||||
// 测试List
|
// 测试List
|
||||||
final Map<String, Object> map = MapBuilder.of(new LinkedHashMap<String, Object>())
|
final Map<String, Object> map = MapBuilder.of(new LinkedHashMap<String, Object>())
|
||||||
.put("Town", ListUtil.of("town1", "town2"))
|
.put("Town", ListUtil.of("town1", "town2"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final Document doc = XmlUtil.mapToXml(map, "City");
|
final Document doc = XmlUtil.mapToXml(map, "City");
|
||||||
Assertions.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" +
|
Assertions.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" +
|
||||||
"<City>" +
|
"<City>" +
|
||||||
"<Town>town1</Town>" +
|
"<Town>town1</Town>" +
|
||||||
"<Town>town2</Town>" +
|
"<Town>town2</Town>" +
|
||||||
"</City>",
|
"</City>",
|
||||||
XmlUtil.toStr(doc));
|
XmlUtil.toStr(doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readTest() {
|
public void readTest() {
|
||||||
final Document doc = XmlUtil.readXML("test.xml");
|
final Document doc = XmlUtil.readXml("test.xml");
|
||||||
Assertions.assertNotNull(doc);
|
Assertions.assertNotNull(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readBySaxTest(){
|
public void readBySaxTest() {
|
||||||
final Set<String> eles = SetUtil.of(
|
final Set<String> eles = SetUtil.of(
|
||||||
"returnsms", "returnstatus", "message", "remainpoint", "taskID", "successCounts");
|
"returnsms", "returnstatus", "message", "remainpoint", "taskID", "successCounts");
|
||||||
XmlUtil.readBySax(ResourceUtil.getStream("test.xml"), new DefaultHandler(){
|
XmlUtil.readBySax(ResourceUtil.getStream("test.xml"), new DefaultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) {
|
public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) {
|
||||||
Assertions.assertTrue(eles.contains(localName));
|
Assertions.assertTrue(eles.contains(localName));
|
||||||
@ -186,8 +187,8 @@ public class XmlUtilTest {
|
|||||||
public void mapToXmlTestWithOmitXmlDeclaration() {
|
public void mapToXmlTestWithOmitXmlDeclaration() {
|
||||||
|
|
||||||
final Map<String, Object> map = MapBuilder.of(new LinkedHashMap<String, Object>())
|
final Map<String, Object> map = MapBuilder.of(new LinkedHashMap<String, Object>())
|
||||||
.put("name", "ddatsh")
|
.put("name", "ddatsh")
|
||||||
.build();
|
.build();
|
||||||
final String xml = XmlUtil.mapToXmlStr(map, true);
|
final String xml = XmlUtil.mapToXmlStr(map, true);
|
||||||
Assertions.assertEquals("<xml><name>ddatsh</name></xml>", xml);
|
Assertions.assertEquals("<xml><name>ddatsh</name></xml>", xml);
|
||||||
}
|
}
|
||||||
@ -195,18 +196,18 @@ public class XmlUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getByPathTest() {
|
public void getByPathTest() {
|
||||||
final String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
final String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
|
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
|
||||||
" <soap:Body>\n" +
|
" <soap:Body>\n" +
|
||||||
" <ns2:testResponse xmlns:ns2=\"http://ws.xxx.com/\">\n" +
|
" <ns2:testResponse xmlns:ns2=\"http://ws.xxx.com/\">\n" +
|
||||||
" <return>2020/04/15 21:01:21</return>\n" +
|
" <return>2020/04/15 21:01:21</return>\n" +
|
||||||
" </ns2:testResponse>\n" +
|
" </ns2:testResponse>\n" +
|
||||||
" </soap:Body>\n" +
|
" </soap:Body>\n" +
|
||||||
"</soap:Envelope>\n";
|
"</soap:Envelope>\n";
|
||||||
|
|
||||||
final Document document = XmlUtil.readXML(xmlStr);
|
final Document document = XmlUtil.readXml(xmlStr);
|
||||||
final Object value = XmlUtil.getByXPath(
|
final Object value = XPathUtil.getByXPath(
|
||||||
"//soap:Envelope/soap:Body/ns2:testResponse/return",
|
"//soap:Envelope/soap:Body/ns2:testResponse/return",
|
||||||
document, XPathConstants.STRING);//
|
document, XPathConstants.STRING);//
|
||||||
Assertions.assertEquals("2020/04/15 21:01:21", value);
|
Assertions.assertEquals("2020/04/15 21:01:21", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +268,7 @@ public class XmlUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void xmlToBeanTest2(){
|
public void xmlToBeanTest2() {
|
||||||
@Data
|
@Data
|
||||||
class SmsRes {
|
class SmsRes {
|
||||||
private String code;
|
private String code;
|
||||||
@ -298,7 +299,7 @@ public class XmlUtilTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Disabled
|
@Disabled
|
||||||
public void formatTest(){
|
public void formatTest() {
|
||||||
// https://github.com/looly/hutool/pull/1234
|
// https://github.com/looly/hutool/pull/1234
|
||||||
final Document xml = XmlUtil.createXml("NODES");
|
final Document xml = XmlUtil.createXml("NODES");
|
||||||
xml.setXmlStandalone(true);
|
xml.setXmlStandalone(true);
|
||||||
@ -325,22 +326,15 @@ public class XmlUtilTest {
|
|||||||
|
|
||||||
parentNode.item(0).appendChild(parent1Node);
|
parentNode.item(0).appendChild(parent1Node);
|
||||||
|
|
||||||
final String format = XmlUtil.toStr(xml,"GBK",true);
|
final String format = XmlUtil.toStr(xml, CharsetUtil.GBK, true);
|
||||||
Console.log(format);
|
Console.log(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapeTest(){
|
public void getParamTest() {
|
||||||
final String a = "<>";
|
|
||||||
final String escape = XmlUtil.escape(a);
|
|
||||||
Console.log(escape);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getParamTest(){
|
|
||||||
final String xml = "<Config name=\"aaaa\">\n" +
|
final String xml = "<Config name=\"aaaa\">\n" +
|
||||||
" <url>222222</url>\n" +
|
" <url>222222</url>\n" +
|
||||||
"</Config>";
|
"</Config>";
|
||||||
|
|
||||||
final Document doc = XmlUtil.parseXml(xml);
|
final Document doc = XmlUtil.parseXml(xml);
|
||||||
final String name = doc.getDocumentElement().getAttribute("name");
|
final String name = doc.getDocumentElement().getAttribute("name");
|
||||||
@ -349,24 +343,23 @@ public class XmlUtilTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Disabled
|
@Disabled
|
||||||
public void issueI5DO8ETest(){
|
public void issueI5DO8ETest() {
|
||||||
// 增加子节点后,格式会错乱,JDK的bug
|
// 增加子节点后,格式会错乱,JDK的bug
|
||||||
XmlUtil.setNamespaceAware(false);
|
|
||||||
final String xmlStr = ResourceUtil.readUtf8Str("issueI5DO8E.xml");
|
final String xmlStr = ResourceUtil.readUtf8Str("issueI5DO8E.xml");
|
||||||
final Document doc = XmlUtil.readXML(xmlStr);
|
final Document doc = XmlUtil.readXml(xmlStr);
|
||||||
|
|
||||||
final Element item = doc.createElement("item");
|
final Element item = doc.createElement("item");
|
||||||
item.setAttribute("id", "cover-image");
|
item.setAttribute("id", "cover-image");
|
||||||
final Element manifestEl = XmlUtil.getElementByXPath("//package/manifest", doc);
|
final Element manifestEl = XPathUtil.getElementByXPath("//package/manifest", doc);
|
||||||
manifestEl.appendChild(item);
|
manifestEl.appendChild(item);
|
||||||
|
|
||||||
Console.log(XmlUtil.format(doc));
|
Console.log(XmlUtil.format(doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void xmlStrToBeanTest(){
|
public void xmlStrToBeanTest() {
|
||||||
final String xml = "<userInfo><name>张三</name><age>20</age><email>zhangsan@example.com</email></userInfo>";
|
final String xml = "<userInfo><name>张三</name><age>20</age><email>zhangsan@example.com</email></userInfo>";
|
||||||
final Document document = XmlUtil.readXML(xml);
|
final Document document = XmlUtil.readXml(xml);
|
||||||
final UserInfo userInfo = XmlUtil.xmlToBean(document, UserInfo.class);
|
final UserInfo userInfo = XmlUtil.xmlToBean(document, UserInfo.class);
|
||||||
Assertions.assertEquals("张三", userInfo.getName());
|
Assertions.assertEquals("张三", userInfo.getName());
|
||||||
Assertions.assertEquals("20", userInfo.getAge());
|
Assertions.assertEquals("20", userInfo.getAge());
|
@ -1,6 +1,7 @@
|
|||||||
package org.dromara.hutool.json;
|
package org.dromara.hutool.json;
|
||||||
|
|
||||||
import org.dromara.hutool.core.io.resource.ResourceUtil;
|
import org.dromara.hutool.core.io.resource.ResourceUtil;
|
||||||
|
import org.dromara.hutool.core.xml.XPathUtil;
|
||||||
import org.dromara.hutool.core.xml.XmlUtil;
|
import org.dromara.hutool.core.xml.XmlUtil;
|
||||||
import org.dromara.hutool.json.xml.JSONXMLSerializer;
|
import org.dromara.hutool.json.xml.JSONXMLSerializer;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
@ -13,7 +14,7 @@ public class IssueI676ITTest {
|
|||||||
public void parseXMLTest() {
|
public void parseXMLTest() {
|
||||||
final JSONObject jsonObject = JSONUtil.parseObj(ResourceUtil.readUtf8Str("issueI676IT.json"));
|
final JSONObject jsonObject = JSONUtil.parseObj(ResourceUtil.readUtf8Str("issueI676IT.json"));
|
||||||
final String xmlStr = JSONXMLSerializer.toXml(jsonObject, null, (String) null);
|
final String xmlStr = JSONXMLSerializer.toXml(jsonObject, null, (String) null);
|
||||||
final String content = String.valueOf(XmlUtil.getByXPath("/page/orderItems[1]/content", XmlUtil.readXML(xmlStr), XPathConstants.STRING));
|
final String content = String.valueOf(XPathUtil.getByXPath("/page/orderItems[1]/content", XmlUtil.readXml(xmlStr), XPathConstants.STRING));
|
||||||
Assertions.assertEquals(content, "bar1");
|
Assertions.assertEquals(content, "bar1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user