diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a4932d02..f9b76c248 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@
* 【core 】 NumberUtil增加isPowerOfTwo方法(pr#1132@Github)
* 【core 】 优化BooleanUtil的校验逻辑(pr#1137@Github)
* 【poi 】 改进sax方式读取逻辑,支持sheetId(issue#1141@Github)
+* 【core 】 XmlUtil增加readBySax方法
### Bug修复
* 【crypto 】 修复SM2验签后无法解密问题(issue#I1W0VP@Gitee)
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java
index 4b9d80a93..357c859c7 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java
@@ -4,6 +4,7 @@ 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;
@@ -13,7 +14,11 @@ 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;
@@ -21,6 +26,8 @@ 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;
@@ -76,6 +83,10 @@ public class XmlUtil {
* 是否打开命名空间支持
*/
private static boolean namespaceAware = true;
+ /**
+ * Sax读取器工厂缓存
+ */
+ private static SAXParserFactory factory;
/**
* 禁用默认的DocumentBuilderFactory,禁用后如果有第三方的实现(如oracle的xdb包中的xmlparse),将会自动加载实现。
@@ -184,6 +195,92 @@ public class XmlUtil {
}
}
+ /**
+ * 使用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);
+ }
+ // 2.从解析工厂获取解析器
+ final SAXParser parse;
+ XMLReader reader;
+ try {
+ parse = factory.newSAXParser();
+ if (contentHandler instanceof DefaultHandler) {
+ parse.parse(source, (DefaultHandler) contentHandler);
+ return;
+ }
+
+ // 3.得到解读器
+ reader = parse.getXMLReader();
+ reader.setContentHandler(contentHandler);
+ reader.parse(source);
+ } catch (ParserConfigurationException | SAXException e) {
+ throw new UtilException(e);
+ } catch (IOException e) {
+ throw new IORuntimeException(e);
+ }
+ }
+
/**
* 将String类型的XML转换为XML文档
*
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/XmlUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/XmlUtilTest.java
index 231de5133..aaa9527ca 100644
--- a/hutool-core/src/test/java/cn/hutool/core/util/XmlUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/XmlUtilTest.java
@@ -9,10 +9,13 @@ import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.w3c.dom.Document;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
import javax.xml.xpath.XPathConstants;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Set;
/**
* {@link XmlUtil} 工具类
@@ -148,6 +151,18 @@ public class XmlUtilTest {
Assert.assertNotNull(doc);
}
+ @Test
+ public void readBySaxTest(){
+ final Set eles = CollUtil.newHashSet(
+ "returnsms", "returnstatus", "message", "remainpoint", "taskID", "successCounts");
+ XmlUtil.readBySax(ResourceUtil.getStream("test.xml"), new DefaultHandler(){
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) {
+ Assert.assertTrue(eles.contains(localName));
+ }
+ });
+ }
+
@Test
public void mapToXmlTestWithOmitXmlDeclaration() {