mirror of
https://gitee.com/dromara/hutool.git
synced 2025-05-03 20:27:58 +08:00
add UniversalNamespaceCache
This commit is contained in:
parent
972234b9d4
commit
3526b39d4f
@ -10,6 +10,7 @@
|
|||||||
* 【poi 】 调整别名策略,clearHeaderAlias和addHeaderAlias同时清除aliasComparator(issue#828@Github)
|
* 【poi 】 调整别名策略,clearHeaderAlias和addHeaderAlias同时清除aliasComparator(issue#828@Github)
|
||||||
* 【core 】 修改StrUtil.equals逻辑,改为contentEquals
|
* 【core 】 修改StrUtil.equals逻辑,改为contentEquals
|
||||||
* 【core 】 增加URLUtil.UrlDecoder
|
* 【core 】 增加URLUtil.UrlDecoder
|
||||||
|
* 【core 】 增加XmlUtil.setNamespaceAware,getByPath支持UniversalNamespaceCache
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【json 】 修复解析JSON字符串时配置无法传递问题
|
* 【json 】 修复解析JSON字符串时配置无法传递问题
|
||||||
|
@ -6,13 +6,17 @@ import cn.hutool.core.exceptions.UtilException;
|
|||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.map.BiMap;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.NamedNodeMap;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
import org.xml.sax.InputSource;
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
import javax.xml.XMLConstants;
|
||||||
|
import javax.xml.namespace.NamespaceContext;
|
||||||
import javax.xml.namespace.QName;
|
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;
|
||||||
@ -68,6 +72,11 @@ public class XmlUtil {
|
|||||||
*/
|
*/
|
||||||
private static String defaultDocumentBuilderFactory = "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl";
|
private static String defaultDocumentBuilderFactory = "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否打开命名空间支持
|
||||||
|
*/
|
||||||
|
private static boolean namespaceAware = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 禁用默认的DocumentBuilderFactory,禁用后如果有第三方的实现(如oracle的xdb包中的xmlparse),将会自动加载实现。
|
* 禁用默认的DocumentBuilderFactory,禁用后如果有第三方的实现(如oracle的xdb包中的xmlparse),将会自动加载实现。
|
||||||
*/
|
*/
|
||||||
@ -75,6 +84,16 @@ public class XmlUtil {
|
|||||||
defaultDocumentBuilderFactory = null;
|
defaultDocumentBuilderFactory = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否打开命名空间支持,默认打开
|
||||||
|
*
|
||||||
|
* @param isNamespaceAware 是否命名空间支持
|
||||||
|
* @since 5.3.1
|
||||||
|
*/
|
||||||
|
synchronized public static void setNamespaceAware(boolean isNamespaceAware) {
|
||||||
|
namespaceAware = isNamespaceAware;
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------- Read
|
// -------------------------------------------------------------------------------------- Read
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,7 +195,7 @@ public class XmlUtil {
|
|||||||
throw new IllegalArgumentException("XML content string is empty !");
|
throw new IllegalArgumentException("XML content string is empty !");
|
||||||
}
|
}
|
||||||
xmlStr = cleanInvalid(xmlStr);
|
xmlStr = cleanInvalid(xmlStr);
|
||||||
return readXML(new InputSource(StrUtil.getReader(xmlStr)));
|
return readXML(StrUtil.getReader(xmlStr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -487,7 +506,7 @@ public class XmlUtil {
|
|||||||
factory = DocumentBuilderFactory.newInstance();
|
factory = DocumentBuilderFactory.newInstance();
|
||||||
}
|
}
|
||||||
// 默认打开NamespaceAware,getElementsByTagNameNS可以使用命名空间
|
// 默认打开NamespaceAware,getElementsByTagNameNS可以使用命名空间
|
||||||
factory.setNamespaceAware(true);
|
factory.setNamespaceAware(namespaceAware);
|
||||||
return disableXXE(factory);
|
return disableXXE(factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,6 +552,7 @@ public class XmlUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取节点所在的Document
|
* 获取节点所在的Document
|
||||||
|
*
|
||||||
* @param node 节点
|
* @param node 节点
|
||||||
* @return {@link Document}
|
* @return {@link Document}
|
||||||
* @since 5.3.0
|
* @since 5.3.0
|
||||||
@ -728,7 +748,31 @@ public class XmlUtil {
|
|||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
*/
|
*/
|
||||||
public static Object getByXPath(String expression, Object source, QName returnType) {
|
public static Object getByXPath(String expression, Object source, 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>
|
||||||
|
* https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html<br>
|
||||||
|
* https://www.ibm.com/developerworks/cn/xml/x-nmspccontext/
|
||||||
|
*
|
||||||
|
* @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(String expression, Object source, QName returnType, NamespaceContext nsContext) {
|
||||||
final XPath xPath = createXPath();
|
final XPath xPath = createXPath();
|
||||||
|
if (null != nsContext) {
|
||||||
|
xPath.setNamespaceContext(nsContext);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (source instanceof InputSource) {
|
if (source instanceof InputSource) {
|
||||||
return xPath.evaluate(expression, (InputSource) source, returnType);
|
return xPath.evaluate(expression, (InputSource) source, returnType);
|
||||||
@ -1182,6 +1226,103 @@ public class XmlUtil {
|
|||||||
}
|
}
|
||||||
return dbf;
|
return dbf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局命名空间上下文<br>
|
||||||
|
* 见:https://www.ibm.com/developerworks/cn/xml/x-nmspccontext/
|
||||||
|
*/
|
||||||
|
public static 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(Node node, 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(Node node, boolean attributesOnly) {
|
||||||
|
NamedNodeMap attributes = node.getAttributes();
|
||||||
|
for (int i = 0; i < attributes.getLength(); i++) {
|
||||||
|
Node attribute = attributes.item(i);
|
||||||
|
storeAttribute(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false == attributesOnly) {
|
||||||
|
NodeList childNodes = node.getChildNodes();
|
||||||
|
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||||
|
Node 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(Node attribute) {
|
||||||
|
// examine the attributes in namespace xmlns
|
||||||
|
if (attribute.getNamespaceURI() != null
|
||||||
|
&& attribute.getNamespaceURI().equals(
|
||||||
|
XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
|
||||||
|
// Default namespace xmlns="uri goes here"
|
||||||
|
if (attribute.getNodeName().equals(XMLConstants.XMLNS_ATTRIBUTE)) {
|
||||||
|
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(String prefix) {
|
||||||
|
if (prefix == null || prefix.equals(XMLConstants.DEFAULT_NS_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(String namespaceURI) {
|
||||||
|
return prefixUri.getInverse().get(namespaceURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<?> getPrefixes(String namespaceURI) {
|
||||||
|
// Not implemented
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
// ---------------------------------------------------------------------------------------- Private method end
|
// ---------------------------------------------------------------------------------------- Private method end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -147,4 +147,22 @@ public class XmlUtilTest {
|
|||||||
String xml = XmlUtil.mapToXmlStr(map, true);
|
String xml = XmlUtil.mapToXmlStr(map, true);
|
||||||
Assert.assertEquals("<xml><name>ddatsh</name></xml>", xml);
|
Assert.assertEquals("<xml><name>ddatsh</name></xml>", xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getByPathTest(){
|
||||||
|
String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
|
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
|
||||||
|
" <soap:Body>\n" +
|
||||||
|
" <ns2:testResponse xmlns:ns2=\"http://ws.xxx.com/\">\n" +
|
||||||
|
" <return>2020/04/15 21:01:21</return>\n" +
|
||||||
|
" </ns2:testResponse>\n" +
|
||||||
|
" </soap:Body>\n" +
|
||||||
|
"</soap:Envelope>\n";
|
||||||
|
|
||||||
|
Document document = XmlUtil.readXML(xmlStr);
|
||||||
|
Object value = XmlUtil.getByXPath(
|
||||||
|
"//soap:Envelope/soap:Body/ns2:testResponse/return",
|
||||||
|
document,XPathConstants.STRING);//
|
||||||
|
Assert.assertEquals("2020/04/15 21:01:21", value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user