cn.hutool.core.util.XmlUtil Maven / Gradle / Ivy
package cn.hutool.core.util;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.BiMap;
import cn.hutool.core.map.MapUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
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.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
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;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.beans.XMLEncoder;
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;
/**
* XML工具类
* 此工具使用w3c dom工具,不需要依赖第三方包。
* 工具类封装了XML文档的创建、读取、写出和部分XML操作
*
* @author xiaoleilu
* @see JAXBUtil
*/
public class XmlUtil {
/**
* 字符串常量: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 String INVALID_REGEX = "[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]";
/**
* 在XML中注释的内容 正则
*/
public static final String COMMENT_REGEX = "(?s)";
/**
* XML格式化输出默认缩进量
*/
public static final int INDENT_DEFAULT = 2;
/**
* 默认的DocumentBuilderFactory实现
*/
private static String defaultDocumentBuilderFactory = "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl";
/**
* 是否打开命名空间支持
*/
private static boolean namespaceAware = true;
/**
* Sax读取器工厂缓存
*/
private static SAXParserFactory factory;
/**
* 禁用默认的DocumentBuilderFactory,禁用后如果有第三方的实现(如oracle的xdb包中的xmlparse),将会自动加载实现。
*/
synchronized public static void disableDefaultDocumentBuilderFactory() {
defaultDocumentBuilderFactory = null;
}
/**
* 设置是否打开命名空间支持,默认打开
*
* @param isNamespaceAware 是否命名空间支持
* @since 5.3.1
*/
synchronized public static void setNamespaceAware(boolean isNamespaceAware) {
namespaceAware = isNamespaceAware;
}
// -------------------------------------------------------------------------------------- Read
/**
* 读取解析XML文件
*
* @param file XML文件
* @return XML文档对象
*/
public static Document readXML(File file) {
Assert.notNull(file, "Xml file is null !");
if (false == file.exists()) {
throw new UtilException("File [{}] not a exist!", file.getAbsolutePath());
}
if (false == file.isFile()) {
throw new UtilException("[{}] not a file!", file.getAbsolutePath());
}
try {
file = file.getCanonicalFile();
} catch (IOException e) {
// ignore
}
BufferedInputStream in = null;
try {
in = FileUtil.getInputStream(file);
return readXML(in);
} finally {
IoUtil.close(in);
}
}
/**
* 读取解析XML文件
* 如果给定内容以“<”开头,表示这是一个XML内容,直接读取,否则按照路径处理
* 路径可以为相对路径,也可以是绝对路径,相对路径相对于ClassPath
*
* @param pathOrContent 内容或路径
* @return XML文档对象
* @since 3.0.9
*/
public static Document readXML(String pathOrContent) {
if (StrUtil.startWith(pathOrContent, '<')) {
return parseXml(pathOrContent);
}
return readXML(FileUtil.file(pathOrContent));
}
/**
* 读取解析XML文件
* 编码在XML中定义
*
* @param inputStream XML流
* @return XML文档对象
* @throws UtilException IO异常或转换异常
* @since 3.0.9
*/
public static Document readXML(InputStream inputStream) throws UtilException {
return readXML(new InputSource(inputStream));
}
/**
* 读取解析XML文件
*
* @param reader XML流
* @return XML文档对象
* @throws UtilException IO异常或转换异常
* @since 3.0.9
*/
public static Document readXML(Reader reader) throws UtilException {
return readXML(new InputSource(reader));
}
/**
* 读取解析XML文件
* 编码在XML中定义
*
* @param source {@link InputSource}
* @return XML文档对象
* @since 3.0.9
*/
public static Document readXML(InputSource source) {
final DocumentBuilder builder = createDocumentBuilder();
try {
return builder.parse(source);
} catch (Exception e) {
throw new UtilException(e, "Parse XML from stream error!");
}
}
/**
* 使用Sax方式读取指定的XML
* 如果用户传入的contentHandler为{@link DefaultHandler},则其接口都会被处理
*
* @param file XML源文件,使用后自动关闭
* @param contentHandler XML流处理器,用于按照Element处理xml
* @since 5.4.4
*/
public static void readBySax(File file, ContentHandler contentHandler) {
InputStream in = null;
try {
in = FileUtil.getInputStream(file);
readBySax(new InputSource(in), contentHandler);
} finally {
IoUtil.close(in);
}
}
/**
* 使用Sax方式读取指定的XML
* 如果用户传入的contentHandler为{@link DefaultHandler},则其接口都会被处理
*
* @param reader XML源Reader,使用后自动关闭
* @param contentHandler XML流处理器,用于按照Element处理xml
* @since 5.4.4
*/
public static void readBySax(Reader reader, ContentHandler contentHandler) {
try {
readBySax(new InputSource(reader), contentHandler);
} finally {
IoUtil.close(reader);
}
}
/**
* 使用Sax方式读取指定的XML
* 如果用户传入的contentHandler为{@link DefaultHandler},则其接口都会被处理
*
* @param source XML源流,使用后自动关闭
* @param contentHandler XML流处理器,用于按照Element处理xml
* @since 5.4.4
*/
public static void readBySax(InputStream source, ContentHandler contentHandler) {
try {
readBySax(new InputSource(source), contentHandler);
} finally {
IoUtil.close(source);
}
}
/**
* 使用Sax方式读取指定的XML
* 如果用户传入的contentHandler为{@link DefaultHandler},则其接口都会被处理
*
* @param source XML源,可以是文件、流、路径等
* @param contentHandler XML流处理器,用于按照Element处理xml
* @since 5.4.4
*/
public static void readBySax(InputSource source, ContentHandler contentHandler) {
// 1.获取解析工厂
if (null == factory) {
factory = SAXParserFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(namespaceAware);
// https://blog.spoock.com/2018/10/23/java-xxe/
try{
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
} catch (final Exception ignore){
// ignore
}
}
// 2.从解析工厂获取解析器
final SAXParser parse;
XMLReader reader;
try {
parse = factory.newSAXParser();
if (contentHandler instanceof DefaultHandler) {
parse.parse(source, (DefaultHandler) contentHandler);
return;
}
// 3.得到解读器
reader = parse.getXMLReader();
// 防止XEE攻击,见:https://www.jianshu.com/p/1a857905b22c
// https://blog.spoock.com/2018/10/23/java-xxe/
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// 忽略外部DTD
reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
// 不包括外部一般实体。
reader.setFeature("http://xml.org/sax/features/external-general-entities",false);
// 不包含外部参数实体或外部DTD子集。
reader.setFeature("http://xml.org/sax/features/external-parameter-entities",false);
reader.setContentHandler(contentHandler);
reader.parse(source);
} catch (ParserConfigurationException | SAXException e) {
throw new UtilException(e);
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 将String类型的XML转换为XML文档
*
* @param xmlStr XML字符串
* @return XML文档
*/
public static Document parseXml(String xmlStr) {
if (StrUtil.isBlank(xmlStr)) {
throw new IllegalArgumentException("XML content string is empty !");
}
xmlStr = cleanInvalid(xmlStr);
return readXML(StrUtil.getReader(xmlStr));
}
// -------------------------------------------------------------------------------------- Write
/**
* 将XML文档转换为String
* 字符编码使用XML文档中的编码,获取不到则使用UTF-8
* 默认非格式化输出,若想格式化请使用{@link #format(Document)}
*
* @param doc XML文档
* @return XML字符串
* @since 5.4.5
*/
public static String toStr(Node doc) {
return toStr(doc, false);
}
/**
* 将XML文档转换为String
* 字符编码使用XML文档中的编码,获取不到则使用UTF-8
* 默认非格式化输出,若想格式化请使用{@link #format(Document)}
*
* @param doc XML文档
* @return XML字符串
*/
public static String toStr(Document doc) {
return toStr((Node) doc);
}
/**
* 将XML文档转换为String
* 字符编码使用XML文档中的编码,获取不到则使用UTF-8
*
* @param doc XML文档
* @param isPretty 是否格式化输出
* @return XML字符串
* @since 5.4.5
*/
public static String toStr(Node doc, boolean isPretty) {
return toStr(doc, CharsetUtil.UTF_8, isPretty);
}
/**
* 将XML文档转换为String
* 字符编码使用XML文档中的编码,获取不到则使用UTF-8
*
* @param doc XML文档
* @param isPretty 是否格式化输出
* @return XML字符串
* @since 3.0.9
*/
public static String toStr(Document doc, boolean isPretty) {
return toStr((Node) doc, isPretty);
}
/**
* 将XML文档转换为String
* 字符编码使用XML文档中的编码,获取不到则使用UTF-8
*
* @param doc XML文档
* @param charset 编码
* @param isPretty 是否格式化输出
* @return XML字符串
* @since 5.4.5
*/
public static String toStr(Node doc, String charset, boolean isPretty) {
return toStr(doc, charset, isPretty, false);
}
/**
* 将XML文档转换为String
* 字符编码使用XML文档中的编码,获取不到则使用UTF-8
*
* @param doc XML文档
* @param charset 编码
* @param isPretty 是否格式化输出
* @return XML字符串
* @since 3.0.9
*/
public static String toStr(Document doc, String charset, boolean isPretty) {
return toStr((Node) doc, charset, isPretty);
}
/**
* 将XML文档转换为String
* 字符编码使用XML文档中的编码,获取不到则使用UTF-8
*
* @param doc XML文档
* @param charset 编码
* @param isPretty 是否格式化输出
* @param omitXmlDeclaration 是否忽略 xml Declaration
* @return XML字符串
* @since 5.1.2
*/
public static String toStr(Node doc, String charset, boolean isPretty, boolean omitXmlDeclaration) {
final StringWriter writer = StrUtil.getWriter();
try {
write(doc, writer, charset, isPretty ? INDENT_DEFAULT : 0, omitXmlDeclaration);
} catch (Exception e) {
throw new UtilException(e, "Trans xml document to string error!");
}
return writer.toString();
}
/**
* 格式化XML输出
*
* @param doc {@link Document} XML文档
* @return 格式化后的XML字符串
* @since 4.4.5
*/
public static String format(Document doc) {
return toStr(doc, true);
}
/**
* 格式化XML输出
*
* @param xmlStr XML字符串
* @return 格式化后的XML字符串
* @since 4.4.5
*/
public static String format(String xmlStr) {
return format(parseXml(xmlStr));
}
/**
* 将XML文档写入到文件
* 使用Document中的编码
*
* @param doc XML文档
* @param absolutePath 文件绝对路径,不存在会自动创建
*/
public static void toFile(Document doc, String absolutePath) {
toFile(doc, absolutePath, null);
}
/**
* 将XML文档写入到文件
*
* @param doc XML文档
* @param path 文件路径绝对路径或相对ClassPath路径,不存在会自动创建
* @param charsetName 自定义XML文件的编码,如果为{@code null} 读取XML文档中的编码,否则默认UTF-8
*/
public static void toFile(Document doc, String path, String charsetName) {
if (StrUtil.isBlank(charsetName)) {
charsetName = doc.getXmlEncoding();
}
if (StrUtil.isBlank(charsetName)) {
charsetName = CharsetUtil.UTF_8;
}
BufferedWriter writer = null;
try {
writer = FileUtil.getWriter(path, CharsetUtil.charset(charsetName), false);
write(doc, writer, charsetName, INDENT_DEFAULT);
} finally {
IoUtil.close(writer);
}
}
/**
* 将XML文档写出
*
* @param node {@link Node} XML文档节点或文档本身
* @param writer 写出的Writer,Writer决定了输出XML的编码
* @param charset 编码
* @param indent 格式化输出中缩进量,小于1表示不格式化输出
* @since 3.0.9
*/
public static void write(Node node, Writer writer, String charset, int indent) {
transform(new DOMSource(node), new StreamResult(writer), charset, indent);
}
/**
* 将XML文档写出
*
* @param node {@link Node} XML文档节点或文档本身
* @param writer 写出的Writer,Writer决定了输出XML的编码
* @param charset 编码
* @param indent 格式化输出中缩进量,小于1表示不格式化输出
* @param omitXmlDeclaration 是否忽略 xml Declaration
* @since 5.1.2
*/
public static void write(Node node, Writer writer, String charset, int indent, boolean omitXmlDeclaration) {
transform(new DOMSource(node), new StreamResult(writer), charset, indent, omitXmlDeclaration);
}
/**
* 将XML文档写出
*
* @param node {@link Node} XML文档节点或文档本身
* @param out 写出的Writer,Writer决定了输出XML的编码
* @param charset 编码
* @param indent 格式化输出中缩进量,小于1表示不格式化输出
* @since 4.0.8
*/
public static void write(Node node, OutputStream out, String charset, int indent) {
transform(new DOMSource(node), new StreamResult(out), charset, indent);
}
/**
* 将XML文档写出
*
* @param node {@link Node} XML文档节点或文档本身
* @param out 写出的Writer,Writer决定了输出XML的编码
* @param charset 编码
* @param indent 格式化输出中缩进量,小于1表示不格式化输出
* @param omitXmlDeclaration 是否忽略 xml Declaration
* @since 5.1.2
*/
public static void write(Node node, OutputStream out, String charset, int indent, boolean omitXmlDeclaration) {
transform(new DOMSource(node), new StreamResult(out), charset, indent, omitXmlDeclaration);
}
/**
* 将XML文档写出
* 格式化输出逻辑参考:https://stackoverflow.com/questions/139076/how-to-pretty-print-xml-from-java
*
* @param source 源
* @param result 目标
* @param charset 编码
* @param indent 格式化输出中缩进量,小于1表示不格式化输出
* @since 4.0.9
*/
public static void transform(Source source, Result result, String charset, int indent) {
transform(source, result, charset, indent, false);
}
/**
* 将XML文档写出
* 格式化输出逻辑参考:https://stackoverflow.com/questions/139076/how-to-pretty-print-xml-from-java
*
* @param source 源
* @param result 目标
* @param charset 编码
* @param indent 格式化输出中缩进量,小于1表示不格式化输出
* @param omitXmlDeclaration 是否忽略 xml Declaration
* @since 5.1.2
*/
public static void transform(Source source, Result result, String charset, int indent, boolean omitXmlDeclaration) {
final TransformerFactory factory = 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 (StrUtil.isNotBlank(charset)) {
xformer.setOutputProperty(OutputKeys.ENCODING, charset);
}
if (omitXmlDeclaration) {
xformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
}
xformer.transform(source, result);
} catch (Exception e) {
throw new UtilException(e, "Trans xml document to string error!");
}
}
// -------------------------------------------------------------------------------------- Create
/**
* 创建XML文档
* 创建的XML默认是utf8编码,修改编码的过程是在toStr和toFile方法里,即XML在转为文本的时候才定义编码
*
* @return XML文档
* @since 4.0.8
*/
public static Document createXml() {
return createDocumentBuilder().newDocument();
}
/**
* 创建 DocumentBuilder
*
* @return DocumentBuilder
* @since 4.1.2
*/
public static DocumentBuilder createDocumentBuilder() {
DocumentBuilder builder;
try {
builder = createDocumentBuilderFactory().newDocumentBuilder();
} catch (Exception e) {
throw new UtilException(e, "Create xml document error!");
}
return builder;
}
/**
* 创建{@link DocumentBuilderFactory}
*
* 默认使用"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"
* 如果使用第三方实现,请调用{@link #disableDefaultDocumentBuilderFactory()}
*
*
* @return {@link DocumentBuilderFactory}
*/
public static DocumentBuilderFactory createDocumentBuilderFactory() {
final DocumentBuilderFactory factory;
if (StrUtil.isNotEmpty(defaultDocumentBuilderFactory)) {
factory = DocumentBuilderFactory.newInstance(defaultDocumentBuilderFactory, null);
} else {
factory = DocumentBuilderFactory.newInstance();
}
// 默认打开NamespaceAware,getElementsByTagNameNS可以使用命名空间
factory.setNamespaceAware(namespaceAware);
return disableXXE(factory);
}
/**
* 创建XML文档
* 创建的XML默认是utf8编码,修改编码的过程是在toStr和toFile方法里,即XML在转为文本的时候才定义编码
*
* @param rootElementName 根节点名称
* @return XML文档
*/
public static Document createXml(String rootElementName) {
return createXml(rootElementName, null);
}
/**
* 创建XML文档
* 创建的XML默认是utf8编码,修改编码的过程是在toStr和toFile方法里,即XML在转为文本的时候才定义编码
*
* @param rootElementName 根节点名称
* @param namespace 命名空间,无则传null
* @return XML文档
* @since 5.0.4
*/
public static Document createXml(String rootElementName, String namespace) {
final Document doc = createXml();
doc.appendChild(null == namespace ? doc.createElement(rootElementName) : doc.createElementNS(namespace, rootElementName));
return doc;
}
// -------------------------------------------------------------------------------------- Function
/**
* 获得XML文档根节点
*
* @param doc {@link Document}
* @return 根节点
* @see Document#getDocumentElement()
* @since 3.0.8
*/
public static Element getRootElement(Document doc) {
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文本中的无效字符
*
* @param xmlContent XML文本
* @return 当传入为null时返回null
*/
public static String cleanInvalid(String xmlContent) {
if (xmlContent == null) {
return null;
}
return xmlContent.replaceAll(INVALID_REGEX, "");
}
/**
* 去除XML文本中的注释内容
*
* @param xmlContent XML文本
* @return 当传入为null时返回null
* @since 5.4.5
*/
public static String cleanComment(String xmlContent) {
if (xmlContent == null) {
return null;
}
return xmlContent.replaceAll(COMMENT_REGEX, StrUtil.EMPTY);
}
/**
* 根据节点名获得子节点列表
*
* @param element 节点
* @param tagName 节点名,如果节点名为空(null或blank),返回所有子节点
* @return 节点列表
*/
public static List getElements(Element element, String tagName) {
final NodeList nodeList = StrUtil.isBlank(tagName) ? element.getChildNodes() : element.getElementsByTagName(tagName);
return transElements(element, nodeList);
}
/**
* 根据节点名获得第一个子节点
*
* @param element 节点
* @param tagName 节点名
* @return 节点
*/
public static Element getElement(Element element, String tagName) {
final NodeList nodeList = element.getElementsByTagName(tagName);
final int length = nodeList.getLength();
if (length < 1) {
return null;
}
for (int i = 0; i < length; i++) {
Element childEle = (Element) nodeList.item(i);
if (childEle == null || childEle.getParentNode() == element) {
return childEle;
}
}
return null;
}
/**
* 根据节点名获得第一个子节点
*
* @param element 节点
* @param tagName 节点名
* @return 节点中的值
*/
public static String elementText(Element element, String tagName) {
Element child = getElement(element, tagName);
return child == null ? null : child.getTextContent();
}
/**
* 根据节点名获得第一个子节点
*
* @param element 节点
* @param tagName 节点名
* @param defaultValue 默认值
* @return 节点中的值
*/
public static String elementText(Element element, String tagName, String defaultValue) {
Element child = getElement(element, tagName);
return child == null ? defaultValue : child.getTextContent();
}
/**
* 将NodeList转换为Element列表
*
* @param nodeList NodeList
* @return Element列表
*/
public static List transElements(NodeList nodeList) {
return transElements(null, nodeList);
}
/**
* 将NodeList转换为Element列表
* 非Element节点将被忽略
*
* @param parentEle 父节点,如果指定将返回此节点的所有直接子节点,null返回所有就节点
* @param nodeList NodeList
* @return Element列表
*/
public static List transElements(Element parentEle, NodeList nodeList) {
int length = nodeList.getLength();
final ArrayList elements = new ArrayList<>(length);
Node node;
Element element;
for (int i = 0; i < length; i++) {
node = nodeList.item(i);
if (Node.ELEMENT_NODE == node.getNodeType()) {
element = (Element) nodeList.item(i);
if (parentEle == null || element.getParentNode() == parentEle) {
elements.add(element);
}
}
}
return elements;
}
/**
* 将可序列化的对象转换为XML写入文件,已经存在的文件将被覆盖
* Writes serializable object to a XML file. Existing file will be overwritten
*
* @param dest 目标文件
* @param bean 对象
*/
public static void writeObjectAsXml(File dest, Object bean) {
XMLEncoder xmlenc = null;
try {
xmlenc = new XMLEncoder(FileUtil.getOutputStream(dest));
xmlenc.writeObject(bean);
} finally {
// 关闭XMLEncoder会相应关闭OutputStream
IoUtil.close(xmlenc);
}
}
/**
* 创建XPath
* Xpath相关文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
*
* @return {@link XPath}
* @since 3.2.0
*/
public static XPath createXPath() {
return XPathFactory.newInstance().newXPath();
}
/**
* 通过XPath方式读取XML节点等信息
* Xpath相关文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
*
* @param expression XPath表达式
* @param source 资源,可以是Docunent、Node节点等
* @return 匹配返回类型的值
* @since 4.0.9
*/
public static Element getElementByXPath(String expression, Object source) {
return (Element) getNodeByXPath(expression, source);
}
/**
* 通过XPath方式读取XML的NodeList
* Xpath相关文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
*
* @param expression XPath表达式
* @param source 资源,可以是Docunent、Node节点等
* @return NodeList
* @since 4.0.9
*/
public static NodeList getNodeListByXPath(String expression, Object source) {
return (NodeList) getByXPath(expression, source, XPathConstants.NODESET);
}
/**
* 通过XPath方式读取XML节点等信息
* Xpath相关文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
*
* @param expression XPath表达式
* @param source 资源,可以是Docunent、Node节点等
* @return 匹配返回类型的值
* @since 4.0.9
*/
public static Node getNodeByXPath(String expression, Object source) {
return (Node) getByXPath(expression, source, XPathConstants.NODE);
}
/**
* 通过XPath方式读取XML节点等信息
* Xpath相关文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
*
* @param expression XPath表达式
* @param source 资源,可以是Docunent、Node节点等
* @param returnType 返回类型,{@link javax.xml.xpath.XPathConstants}
* @return 匹配返回类型的值
* @since 3.2.0
*/
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节点等信息
* Xpath相关文章:
* https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
* 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();
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 (XPathExpressionException e) {
throw new UtilException(e);
}
}
/**
* 转义XML特殊字符:
*
*
* & (ampersand) 替换为 &
* < (小于) 替换为 <
* > (大于) 替换为 >
* " (双引号) 替换为 "
*
*
* @param string 被替换的字符串
* @return 替换后的字符串
* @since 4.0.8
*/
public static String escape(String string) {
return EscapeUtil.escapeHtml4(string);
}
/**
* 反转义XML特殊字符:
*
* @param string 被替换的字符串
* @return 替换后的字符串
* @see EscapeUtil#unescape(String)
* @since 5.0.6
*/
public static String unescape(String string) {
return EscapeUtil.unescapeHtml4(string);
}
/**
* XML格式字符串转换为Map
*
* @param xmlStr XML字符串
* @return XML数据转换后的Map
* @since 4.0.8
*/
public static Map xmlToMap(String xmlStr) {
return xmlToMap(xmlStr, new HashMap<>());
}
/**
* XML转Java Bean
*
* @param bean类型
* @param node XML节点
* @param bean bean类
* @return bean
* @see JAXBUtil#xmlToBean(String, Class)
* @since 5.2.4
*/
public static T xmlToBean(Node node, Class bean) {
final Map map = xmlToMap(node);
if (null != map && map.size() == 1) {
final String simpleName = bean.getSimpleName();
final String nodeName = CollUtil.getFirst(map.keySet());
if (simpleName.equalsIgnoreCase(nodeName)) {
// 只有key和bean的名称匹配时才做单一对象转换
return BeanUtil.toBean(map.get(nodeName), bean);
}
}
return BeanUtil.toBean(map, bean);
}
/**
* XML格式字符串转换为Map
*
* @param node XML节点
* @return XML数据转换后的Map
* @since 4.0.8
*/
public static Map xmlToMap(Node node) {
return xmlToMap(node, new HashMap<>());
}
/**
* XML格式字符串转换为Map
* 只支持第一级别的XML,不支持多级XML
*
* @param xmlStr XML字符串
* @param result 结果Map类型
* @return XML数据转换后的Map
* @since 4.0.8
*/
public static Map xmlToMap(String xmlStr, Map result) {
final Document doc = parseXml(xmlStr);
final Element root = getRootElement(doc);
root.normalize();
return xmlToMap(root, result);
}
/**
* XML节点转换为Map
*
* @param node XML节点
* @param result 结果Map类型
* @return XML数据转换后的Map
* @since 4.0.8
*/
@SuppressWarnings("unchecked")
public static Map xmlToMap(Node node, Map 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 (false == isElement(childNode)) {
continue;
}
childEle = (Element) childNode;
final Object value = result.get(childEle.getNodeName());
Object newValue;
if (childEle.hasChildNodes()) {
// 子节点继续递归遍历
final Map map = xmlToMap(childEle);
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