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() {