前言

大家在工作中不知道有没有遇到过类似的情况:
1、需要写代码解析一个xml文件时,发现需要导入dom4j的jar包,一时又找不到,然后还得浪费几分钟时间去找jar包。
2、好不容易找到了jar包,写了一段程序很适合本次使用,但是下次使用的时候还得重新写,代码复用率低。
3、从网上找到了一段别人写的解析xml文件的代码,copy下来发现好多地方还得修改才能用。

那么为什么不自己趁着摸鱼的时候写一个工具包呢,把自己常用的工具集成进去,之后写代码复用性更高,并且可以高度定制化,而且对底层实现更了解,虽然这属于重复造轮子,但是代码能立就是通过重复造轮子来提升的。

下面介绍一个我目前实现的xml解析工具,不需要导第三方jar包,通过jdk自带的Dom工具来实现。

xml解析工具实现:

package pers.cz.tools;import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import pers.cz.io.Charsets;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @program: JefConfig* @description: xml文档解析工具* @author: Cheng Zhi* @create: 2023-04-20 12:48**/
public class XmlUtils {private static final String DEFAULT_CHATSET = "UTF-8";/*** 缓存DocumentBuilderFactory, 其中T 标识忽略注释与否, 第二个T 标识是否解析域名*/private static DocumentBuilderFactory domFactoryTT;private static DocumentBuilderFactory domFactoryTF;private static DocumentBuilderFactory domFactoryFT;private static DocumentBuilderFactory domFactoryFF;private static Map<String, DocumentBuilderFactory> DocumentBuilderFactoryCache = new ConcurrentHashMap<String, DocumentBuilderFactory>(8);private static final ThreadLocal<DocumentBuilderCache> REUSABLE_BUILDER = new ThreadLocal<DocumentBuilderCache>() {@Overrideprotected DocumentBuilderCache initialValue() {return new DocumentBuilderCache();}};private static class DocumentBuilderCache {DocumentBuilder cacheTT;DocumentBuilder cacheTF;DocumentBuilder cacheFT;DocumentBuilder cacheFF;/*** 根据传入的特性,提供满足条件的DocumentBuilder** @param ignorComments* @param namespaceAware* @return*/private DocumentBuilder getDocumentBuilder(boolean ignorComments, boolean namespaceAware) {if (ignorComments && namespaceAware) {if (DocumentBuilderFactoryCache.get("domFactoryTT") == null) {DocumentBuilderFactoryCache.put("doFactoryTT", initFactory(true, true));}if (cacheTT == null) {cacheTT = initBuilder(DocumentBuilderFactoryCache.get("domFactoryTT"));}return cacheTT;} else if (ignorComments) {if (DocumentBuilderFactoryCache.get("domFactoryTF") == null) {DocumentBuilderFactoryCache.put("domFactoryTF", initFactory(true, true));}if (cacheTF == null) {cacheTF = initBuilder(DocumentBuilderFactoryCache.get("domFactoryTF"));}return cacheTF;} else if (namespaceAware) {if (DocumentBuilderFactoryCache.get("domFactoryFT") == null) {DocumentBuilderFactoryCache.put("domFactoryFT", initFactory(true, true));}if (cacheFT == null) {cacheFT = initBuilder(DocumentBuilderFactoryCache.get("domFactoryFT"));}return cacheFT;} else {if (DocumentBuilderFactoryCache.get("domFactoryFF") == null) {DocumentBuilderFactoryCache.put("domFactoryFF", initFactory(true, true));}if (cacheFF == null) {cacheFF = initBuilder(DocumentBuilderFactoryCache.get("domFactoryFF"));}return cacheFF;}}private DocumentBuilder initBuilder(DocumentBuilderFactory domFactory) {DocumentBuilder builder;try {builder = domFactory.newDocumentBuilder();} catch (ParserConfigurationException e) {throw new UnsupportedOperationException(e);}return builder;}}private static DocumentBuilderFactory initFactory(boolean ignorComments, boolean namespaceAware) {DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();documentBuilderFactory.setIgnoringComments(ignorComments);documentBuilderFactory.setNamespaceAware(namespaceAware);return documentBuilderFactory;}public static Document loadDocument(String filePath, boolean ignoreComment) throws IOException, SAXException, ParserConfigurationException {return loadDocument(new FileInputStream(new File(filePath)), DEFAULT_CHATSET, ignoreComment);}public static Document loadDocument(InputStream in, String charSet, boolean ignoreComment) throws ParserConfigurationException, IOException, SAXException {return loadDocument(in, charSet, ignoreComment, false);}/*** 加载xml文件* @param in* @param charSet          字符编码* @param ignoreComment    是否忽略注释* @return*/public static Document loadDocument(InputStream in, String charSet, boolean ignoreComment, boolean namespaceAware) throws ParserConfigurationException, IOException, SAXException {DocumentBuilder db = REUSABLE_BUILDER.get().getDocumentBuilder(ignoreComment, namespaceAware);InputSource is = null;// 解析流来获取charsetif (charSet == null) {// 读取头200个字节来分析编码charSet = DEFAULT_CHATSET;}if (charSet != null) {is = new InputSource(new XmlFixedReader(new InputStreamReader(in, charSet)));is.setEncoding(charSet);} else { // 自动检测编码Reader reader = new InputStreamReader(in, "UTF-8");// 为了过滤XML当中的非法字符,所以要转换为Reader,又为了转换为Reader,所以要获得XML的编码is = new InputSource(new XmlFixedReader(reader));}Document doc = db.parse(is);doc.setXmlStandalone(true);// 避免出现standalone="no"return doc;}/*** 通过读取XML头部文字来判断xml文件的编码** @param buf*            XML文件头部若干字节* @param len*            判定长度* @return 获得XML编码。如果不成功返回null。*/public static String getCharsetInXml(byte[] buf, int len) {String s = new String(buf).toLowerCase();int n = s.indexOf("encoding=");if (n > -1) {s = s.substring(n + 9);if (s.charAt(0) == '"' || s.charAt(0) == ''') {s = s.substring(1);}n = s.indexOf(""' ><");if (n > -1) {s = s.substring(0, n);}if (StringUtils.isEmpty(s)) {return null;}s = Charsets.getStdName(s);return s;} else {return null;}}/*** 过滤xml的无效字符。* <p/>* XML中出现以下字符就是无效的,此时Parser会抛出异常,仅仅因为个别字符导致整个文档无法解析,是不是小题大作了点?* 为此编写了这个类来过滤输入流中的非法字符。* 不过这个类的实现不够好,性能比起原来的Reader实现和nio的StreamReader下降明显,尤其是read(char[] b, int* off, int len)方法. 如果不需要由XmlFixedReader带来的容错性,还是不要用这个类的好。* <ol>* <li>0x00 - 0x08</li>* <li>0x0b - 0x0c</li>* <li>0x0e - 0x1f</li>* </ol>*/public static class XmlFixedReader extends FilterReader {public XmlFixedReader(Reader reader) {super(new BufferedReader(reader));}public int read() throws IOException {int ch = super.read();while ((ch >= 0x00 && ch <= 0x08) || (ch >= 0x0b && ch <= 0x0c) || (ch >= 0x0e && ch <= 0x1f) || ch == 0xFEFF) {ch = super.read();}return ch;}// 最大的问题就是这个方法,一次读取一个字符速度受影响。public int read(char[] b, int off, int len) throws IOException {if (b == null) {throw new NullPointerException();} else if (off < 0 || len < 0 || len > b.length - off) {throw new IndexOutOfBoundsException();} else if (len == 0) {return 0;}int c = read();if (c == -1) {return -1;}b[off] = (char) c;int i = 1;try {for (; i < len; i++) {c = read();if (c == -1) {break;}b[off + i] = (char) c;}} catch (IOException ee) {}return i;}}
}

自己编写xml的好处:
1、由于DocumentBuilderFactory在创建实例的使用只支持反射,反射的性能相对较低,因此自定义xml解析工具中提供了DocumentBuilderFactoryCache用来缓存改工厂实例,如果程序只调用一次那么这个缓存的作用不大,如果程序中尤其是集成到工程中时,多个地方反复调用xml解析器,此时DocumentBuilderFactoryCache的优势便体现了出来,这会大大节省实例创建的时间。

2、提供高度定制化方式获取Document,支持InputStream,和文件路径的方式,如果需要还可以继续扩展。
3、笔者通过自己写工具更加了解了dom4j解析文件的原理,之后使用dom4j之类的工具包也更得心应手。

高效的Xml解析工具相关推荐

  1. 高效的XML解析与组装-fasterxml

    高效的XML解析与组装-fasterxml 写在前面 初步的解析工具选择-XStream fasterxml的使用 请求XML pom.xml 报文的实体类定义 组装以及解析工具 后记 写在前面 最近 ...

  2. Java XML解析工具 dom4j介绍及使用实例

    Java XML解析工具 dom4j介绍及使用实例 dom4j介绍 dom4j的项目地址:http://sourceforge.net/projects/dom4j/?source=directory ...

  3. Java XML解析工具 JDOM介绍及使用实例

    Java XML解析工具 JDOM JDOM介绍 JDOM是一种使用XML的独特Java工具包,用于快速开发XML应用程序. 它的设计包含Java语言的语法乃至语义. JDOM是一个开源项目,它基于树 ...

  4. 精短高效的XML解析器,纯C单一程序,应用于银行的国税库行横向联网接口系统中,稳定可靠,运行速度飞快

    来源于:http://www.ej38.com/showinfo/c-xml-169407.html 供各位参考 精短高效的XML解析器,纯C单一程序,应用于银行的国税库行横向联网接口系统中,稳定可靠 ...

  5. Java XML解析工具类

    Java XML解析工具类 Java解析XML的方式有很多,这里不一一说明了,利用三方jar包,实现了一个XML工具类 本身是有个需求,讲三方公司的XML请求文件中的Response里的属性赋值成ja ...

  6. java 之 xml 解析工具

    java 解析 xml 文件 转载自 : http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/17/3082949.html 众所周知,现在解析X ...

  7. [转]xml解析工具的效率比较QDomDocument、TinyXml-2、RapidXml、PugiXml

    转自:http://www.itdaan.com/blog/2017/02/20/301ad47832f4.html 由于windows环境下测试不稳定,博主选择在linux下进行的测试! Qt - ...

  8. JSqlparser 使用攻略(高效的SQL解析工具)

    JSqlparser github地址 目录 Maven 引用 远程仓库 依赖包 SQL解析 获取SQL中的信息 创建Select的方式 创建Select(非SQL String 创建) Insert ...

  9. java xml开源操作类,xml解析和操作的开源工具项目涵盖java c++ php 等语言

    XML解析器-Xerces    XML操作库-dom4j    XML文档解析器-Digester    J2ME-的XML-解析器-kXML XML解析类库-MXP1    XML解析器-LibX ...

最新文章

  1. 【Android 安全】DEX 加密 ( Application 替换 | Android 应用启动原理 | LoadedApk 源码分析 )
  2. java模拟国际象棋游戏_java国际象棋小游戏
  3. 计篇-之一文言文翻译
  4. Python3经典100道练习题003
  5. Taro+react开发(23)--componentWillReceiveProps
  6. mac php errorlog,Mac下使用php的error_log()函数发送邮件
  7. htmlplay前端编辑器下载_2019年最好用的代码编辑器推荐
  8. linux centos7 r安装,如何在CentOS 7上安装R
  9. python函数可变长参数_day14 Python函数之可变长参数
  10. docker中的mysql操作
  11. Docker 操作命令 整理
  12. IntellIJ IDEA 配置 Vue 支持
  13. jsp加载常量的探讨
  14. 详解: Spark 相对于MapReduce的优势(为什么MapReduce性能不理想)
  15. 通过OPENSSL建立证书以及CSR证书签名过程
  16. Error while extracting response for type [] and content type [],json返回值被解析为xml
  17. 提莫攻击 findPoisonedDuration
  18. 阿里云centos下载地址
  19. 装系统时遇到的一些坑
  20. 任正非:宁赔10亿,也要辞退7000员工!华为卸磨杀驴?却获网友点赞!

热门文章

  1. list排序方法汇总
  2. Android使用Retrofit技术仿微信图片上传,可以选择多张图片拍照上传
  3. 无法将“keytool”项识别为 cmdlet 函数、脚本文件或可运行程序的名称
  4. 定了!11月起,网易将为本号粉丝提供数据分析培训,费用全免!
  5. 09 PDM行业开门红 中船重工重庆川东造船厂联手迈特科技 实施PDM项目 加速船舶业发展
  6. 操作系统PV操作伪代码(小和尚提水、老和尚饮水问题)
  7. 如何加入鸿蒙生态,【新机】魅族宣布加入鸿蒙生态,海报文案又引争议丨荣耀100W充电器谍照曝光...
  8. STC15w2k58s4使用官方ISP电路无法下载的解决办法
  9. 2500元的办公用计算机,2500左右的电脑配置
  10. 保姆级教程,手把手教你批量打造小红书爆文笔记,快来收藏