Jacoco XML 解析
1 XML解析器对比
1. DOM解析器:
○ 优点:易于使用,提供完整的文档树,可以方便地修改和遍历XML文档。
○ 缺点:对大型文档消耗内存较多,加载整个文档可能会变慢。
○ 适用场景:适合小型XML文档或需要多次访问和修改XML内容的情况。
2. SAX解析器:
○ 优点:逐个处理XML元素,节省内存,适用于一次性遍历大型XML文档。
○ 缺点:编写处理事件的代码可能会相对复杂,不适合需要频繁修改XML内容的情况。
○ 适用场景:适合大型XML文档的读取、分析和提取数据。
3. StAX解析器:
○ 优点:结合了DOM和SAX的优点,提供了灵活的编程模型,适用于流式处理和部分内存加载。
○ 缺点:可能比纯粹的SAX稍微复杂一些。
○ 适用场景:适合需要处理中等大小的XML文档,同时保持较低内存占用的情况。
4. XPath解析器:
○ 优点:提供了强大的查询功能,能够方便地定位XML文档中的元素和数据。
○ 缺点:可能会稍微降低性能,特别是在复杂的查询情况下。
○ 适用场景:适合需要定位和提取特定数据的情况,可以减少手动遍历和解析的工作。
5. JSON对应XML解析器:
○ 优点:可以将XML数据转换为JSON格式,利用JSON解析器处理。
○ 缺点:可能会导致数据结构转换复杂性,不是所有情况下都适用。
○ 适用场景:当您的应用程序使用JSON格式处理数据,但需要处理XML数据时,可以考虑此类解析器。
解析的jacoco XML数据量较多 行数至几十万行 XML大小为 数十M
选择SAX解析器进行解析
2 解析思路
SAX可以逐行处理标签




/*** @author xieyan* @date 2023-08-22 16:13*/
public class JacocoXmlConstant {public static final String COUNTER = "counter";public static final String METHOD = "method";public static final String CLASS = "class";public static final String SOURCEFILE = "sourcefile";public static final String SOURCEFILE_NAME = "name";public static final String COUNTER_TYPE = "type";public static final String COUNTER_MISSED = "missed";public static final String COUNTER_COVERED = "covered";public static final String METHOD_NAME = "name";public static final String METHOD_DESC = "desc";public static final String CLASS_NAME = "name";public static final String CLASS_SOURCEFILENAME = "sourcefilename";public static final String REPORT_NAME = "name";public static final String PACKAGE_NAME = "name";public static final String REPORT = "report";public static final String PACKAGE = "package";public static final String SESSIONINFO = "sessioninfo";public static final String LINE = "line";public static final String INSTRUCTION_TYPE = "INSTRUCTION";public static final String BRANCH_TYPE = "BRANCH";public static final String LINE_TYPE = "LINE";public static final String COMPLEXITY_TYPE = "COMPLEXITY";public static final String METHOD_TYPE = "METHOD";public static final String CLASS_TYPE = "CLASS";}import lombok.Getter;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;import java.util.List;import static com.jacoco.xml.JacocoXmlConstant.*;/*** 解析xml文件** @author xieyan*/
@Getter
public class ParseXmlHandler extends DefaultHandler {private Report report;private Package currentPackage;private Clazz currentClass;private Method currentMethod;private Counter currentCounter;private String sourcefile;/*** 开始解析元素时触发*/@Overridepublic void startElement(String uri, String localName, String qName, Attributes attributes) {switch (qName) {// 跳过 sessioninfo 和 linecase SESSIONINFO:case LINE:return;// 解析reportcase REPORT:report = new Report();report.setName(attributes.getValue(REPORT_NAME));break;// 解析packagecase PACKAGE:currentPackage = new Package();currentPackage.setName(attributes.getValue(PACKAGE_NAME));if (report != null) {report.addPackage(currentPackage);}break;// 解析classcase CLASS:currentClass = new Clazz();currentClass.setName(attributes.getValue(CLASS_NAME));currentClass.setSourcefilename(attributes.getValue(CLASS_SOURCEFILENAME));if (currentPackage != null) {currentPackage.addClass(currentClass);}break;// 解析methodcase METHOD:currentMethod = new Method();currentMethod.setName(attributes.getValue(METHOD_NAME));currentMethod.setDesc(attributes.getValue(METHOD_DESC));if (currentClass != null) {currentClass.addMethod(currentMethod);}break;// 解析sourcefilecase SOURCEFILE:sourcefile = attributes.getValue(SOURCEFILE_NAME);break;// 解析countercase COUNTER:currentCounter = new Counter();currentCounter.setType(attributes.getValue(COUNTER_TYPE));currentCounter.setMissed(Integer.parseInt(attributes.getValue(COUNTER_MISSED)));currentCounter.setCovered(Integer.parseInt(attributes.getValue(COUNTER_COVERED)));// 绑定counter 到对应的元素bindCounter();break;default:break;}}/*** 结束解析元素时触发*/@Overridepublic void endElement(String uri, String localName, String qName) {switch (qName) {case SESSIONINFO:case LINE:// 跳过 sessioninfo 和 linereturn;case SOURCEFILE:// 避免重复加入 counter 到 pagesourcefile = null;break;case REPORT:// 计算总的覆盖率Coverage coverageReport = caculateCoverage(report.getReportCounters());report.setReportCoverage(coverageReport);break;case PACKAGE:// 计算 package 覆盖率if (currentPackage != null) {Coverage coveragePackage = caculateCoverage(currentPackage.getPackageCounters());currentPackage.setPackageCoverage(coveragePackage);currentPackage = null;}break;case CLASS:// 计算 class 覆盖率if (currentClass != null) {Coverage coverageClass = caculateCoverage(currentClass.getClazzCounters());currentClass.setClazzCoverage(coverageClass);currentClass = null;}break;case METHOD:// 计算 method 覆盖率if (currentMethod != null) {Coverage coverageMethod = caculateCoverage(currentMethod.getMethodCounters());currentMethod.setMethodCoverage(coverageMethod);currentMethod = null;}break;case COUNTER:currentCounter = null;break;default:break;}}/*** 绑定counter 到对应的元素*/private void bindCounter() {// counter 属于 methodif (currentMethod != null) {currentMethod.addCounter(currentCounter);}// counter 属于 classelse if (currentClass != null) {currentClass.addCounter(currentCounter);}// counter 属于 packageelse if (currentPackage != null) {// 跳过sourcefile里的counter 避免重复加入if (sourcefile == null) {currentPackage.addCounter(currentCounter);}}// counter 属于 reportelse if (report != null) {report.addCounter(currentCounter);}}/*** 计算覆盖率*/private Coverage caculateCoverage(List<Counter> counterList) {Coverage result = new Coverage();for (Counter counter : counterList) {String type = counter.getType().toUpperCase();String coverage = processResult(counter);setCoverageByType(result, type, coverage);}return result;}private void setCoverageByType(Coverage result, String type, String coverage) {switch (type) {// 指令覆盖率case INSTRUCTION_TYPE:result.setInstructionCoverage(coverage);break;// 行覆盖率case LINE_TYPE:result.setLineCoverage(coverage);break;// 分支覆盖率case BRANCH_TYPE:result.setBranchCoverage(coverage);break;// 圈复杂度覆盖率case COMPLEXITY_TYPE:result.setComplexityCoverage(coverage);break;// 方法覆盖率case METHOD_TYPE:result.setMethodCoverage(coverage);break;// 类覆盖率case CLASS_TYPE:result.setClassCoverage(coverage);break;default:break;}}/*** 处理覆盖率结果* 保留两位小数 例如 99.99%*/private String processResult(Counter counter) {int missed = counter.getMissed();int covered = counter.getCovered();double coverage = (double) covered / (missed + covered);double coveragePercentage = coverage * 100;return String.format("%.2f%%", coveragePercentage);}}import lombok.Data;import java.util.ArrayList;
import java.util.List;/*** @author xieyan*/
@Data
public class Clazz {private String name;private String sourcefilename;private final List<Method> methods = new ArrayList<>();private final List<Counter> clazzCounters = new ArrayList<>();private Coverage clazzCoverage;public void addMethod(Method method) {methods.add(method);}public void addCounter(Counter counter) {clazzCounters.add(counter);}}import lombok.Data;/*** @author xieyan*/
@Data
public class Counter {private String type;private int missed;private int covered;}import lombok.Data;/*** 覆盖率统计* @author xieyan* @date 2021-08-22 16:13*/
@Data
public class Coverage {/*** 指令覆盖率*/private String instructionCoverage;/** * 分支覆盖率*/private String branchCoverage;/*** 行覆盖率*/private String lineCoverage;/*** 圈复杂度覆盖率*/private String complexityCoverage;/*** 方法覆盖率 */private String methodCoverage;/*** 类覆盖率*/private String classCoverage;
}Method
import lombok.Data;import java.util.ArrayList;
import java.util.List;/*** @author xieyan*/
@Data
public class Method {private String name;private String desc;private final List<Counter> methodCounters = new ArrayList<>();private Coverage methodCoverage;public void addCounter(Counter counter) {methodCounters.add(counter);}}import lombok.Data;import java.util.ArrayList;
import java.util.List;/*** @author xieyan*/
@Data
public class Package {private String name;private final List<Clazz> classes = new ArrayList<>();private final List<Counter> packageCounters = new ArrayList<>();private Coverage packageCoverage;public void addClass(Clazz clazz) {classes.add(clazz);}public void addCounter(Counter counter) {packageCounters.add(counter);}}@Data
public class Report {private String name;private final List<Package> packages = new ArrayList<>();private final List<Counter> reportCounters = new ArrayList<>();private Coverage reportCoverage;public void addPackage(Package pkg) {packages.add(pkg);}public void addCounter(Counter counter) {reportCounters.add(counter);}}import lombok.extern.slf4j.Slf4j;
import org.xml.sax.SAXException;import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;/*** 解析jacoco覆盖率报告工具类* @author xieyan* @date 2023-08-22 16:38*/
@Slf4j
public class ParseJacocoXmlUtil {/*** 解析jacoco覆盖率报告** @param xmlPath jacoco覆盖率报告路径* @return 解析后的报告对象,解析失败时返回null*/public static Report parse(String xmlPath) {try {SAXParserFactory factory = SAXParserFactory.newInstance();// 禁用DTD验证factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);SAXParser saxParser = factory.newSAXParser();// 读取xml文件File xmlFile = new File(xmlPath);ParseXmlHandler handler = new ParseXmlHandler();try (InputStream inputStream = xmlFile.toURI().toURL().openStream()) {// 解析XML文件saxParser.parse(inputStream, handler);return handler.getReport();}} catch (ParserConfigurationException | SAXException | IOException e) {log.error("解析jacoco xml 覆盖率报告失败", e);}return null;}
}public class SaxParsingToJavaObjectExample {public static void main(String[] args) {// 指定XML文件路径String xmlFilePath = "D:\\tmp\\jacoco.xml";//String xmlFilePath = "F:\\temp\\a.xml";Report report = ParseJacocoXmlUtil.parse(xmlFilePath);Optional.ofNullable(report).ifPresent(SaxParsingToJavaObjectExample::export);}public static void export(Report report) {for (Package pkg : report.getPackages()) {System.out.println("Package Name: " + pkg.getName());for (Clazz clazz : pkg.getClasses()) {System.out.println("Class Name: " + clazz.getName());for (Method method : clazz.getMethods()) {System.out.println("Method Name: " + method.getName());System.out.println("Method Description: " + method.getDesc());for (Counter counter : method.getMethodCounters()) {System.out.println("Counter Type: " + counter.getType());System.out.println("Counter Missed: " + counter.getMissed());System.out.println("Counter Covered: " + counter.getCovered() + "\n");}System.out.println("==================== Method ==========================================");}}}}
}相关文章:
Jacoco XML 解析
1 XML解析器对比 1. DOM解析器: ○ 优点:易于使用,提供完整的文档树,可以方便地修改和遍历XML文档。 ○ 缺点:对大型文档消耗内存较多,加载整个文档可能会变慢。 ○ 适用场景:适合小型XML文档…...
【面试题】JDK(工具包)、JRE(运行环境和基础库)、JVM(java虚拟机)之间的关系?
【面试题】JDK、JRE、JVM之间的关系? JDK(Java Development Kit):Java开发工具包,提供给Java程序员使用,包含了JRE,同时还包含了编译器javac与自带的调试工具Jconsole、jstack等。 JRE(Java Runtime Environment):Java运行时环境&…...
软件设计师学习笔记7-输入输出技术+总线+可靠性+性能指标
目录 1.输入输出技术 1.1数据传输控制方式 1.2中断处理过程 2.总线 3.可靠性 3.1可靠性指标 3.2串联系统与并联系统 3.3混合模型 4.性能指标 1.输入输出技术 即CPU控制主存与外设交互的过程 1.1数据传输控制方式 (1)程序控制(查询)方式&…...
Windows下MATLAB调用Python函数操作说明
MATLAB与Python版本的兼容 具体可参看MATLAB与Python版本的兼容 操作说明 操作说明请参看下面两个链接: 操作指南 简单说明: 我安装的是MATLAB2022a和Python3.8.6(安装时请勾选所有可以勾选的,包括路径)。对应版本安…...
【android12-linux-5.1】【ST芯片】驱动与HAL移植后数据方向异常
ST的传感器驱动与HAL一直成功后,能拿到数据了,但是设备是横屏,系统默认是竖屏。就会出现屏幕自动转动时方向是错的的情况,设备横立展示的是竖屏,设备竖立展示的是横屏。 这个是PCB上设计的传感器贴片方向和横屏不一致…...
JavaScript Es6_3笔记
JavaScript 进阶 文章目录 JavaScript 进阶编程思想面向过程面向对象 构造函数原型对象constructor 属性对象原型原型继承原型链 了解构造函数原型对象的语法特征,掌握 JavaScript 中面向对象编程的实现方式,基于面向对象编程思想实现 DOM 操作的封装。 …...
Qt产生随机数
Qt产生随机数 提问:注意:实现: 提问: 有没有小伙伴遇到这么一种情况,使用rand()和qrand()函数生成的随机数好像不是那么随机,每次都一样。那这种就叫做“伪随机”,因为没有种随机数种子&#x…...
postgresql常用函数-数学函数
postgresql常用函数 简介数学函数算术运算符绝对值取整函数乘方与开方指数与对数整数商和余数弧度与角度常量 π符号函数生成随机数 简介 函数(function)是一些预定义好的代码模块,可以将输入进行计算和处理,最终输出一个 结果值…...
【排序】快速排序(前后指针法)—— 考的最少的一种算法
以从小到大的顺序进行说明。 前后指针法 是指对于一个数组,定义前后各一个指针(prev 和 cur) prev用于卡一个比基准值大的值进行交换cur用于向前遍历出比基准值小的,和prev进行交换 图解 初始化 选出基准值4 如果cur 所在的值…...
软考:中级软件设计师:关系代数:中级软件设计师:关系代数,规范化理论函数依赖,它的价值和用途,键,范式,模式分解
软考:中级软件设计师:关系代数 提示:系列被面试官问的问题,我自己当时不会,所以下来自己复盘一下,认真学习和总结,以应对未来更多的可能性 关于互联网大厂的笔试面试,都是需要细心准备的 &…...
openCV实战-系列教程2:阈值与平滑处理(图像阈值/图像平滑处理/高斯/中值滤波)、源码解读
1、图像阈值 t图像阈值函数,就是需要判断一下像素值大于一个数应该怎么处理,小于一个数应该怎么处理 ret, dst cv2.threshold(src, thresh, maxval, type) 参数解析: src: 原始输入图,只能输入单通道图像&#…...
C语言第五章-循环结构练习
1、设计一个小型模拟彩票中奖机,已知彩票中奖号码是一个固定的3位数(原始号码)。对任意一个3位数,取出它的每位数字和原始号码的每位数字比较,有1位数相同中三等奖,有2位数相同中二等奖,有3位数…...
Echarts面积图2.0(范围绘制)
代码: // 以下代码可以直接粘贴在echarts官网的示例上 // 范围值 let normalValue {type: 内部绘制,minValue: 200,maxValue: 750 } // 原本的绘图数据 let seriesData [820, 932, 901, 934, 1290, 1330, 1320] let minData Array.from({length: seriesData.len…...
flink checkpoint时exact-one模式和atleastone模式的区别
背景: flink在开启checkpoint的时候有两种模式可以选择,exact-one和atleastone模式,那么这两种模式有什么区别呢? exact-one和atleastone模式的区别 先说结论:exact-one可以完全做到状态的一致性,而atle…...
QEMU 仿真RISC-V freeRTOS 程序
1. 安裝RISC-V 仿真環境 --QEMU 安裝包下載地址: https://www.qemu.org/ 安裝命令及安裝成功效果如下所示, target-list 設定爲riscv32-softmmu, $ cat ~/project/qemu-8.0.4/install.sh sudo apt-get install libglib2.0-dev sudo apt-get install libpixman-1-dev ./co…...
用大白话来讲讲多线程的知识架构
感觉多线程的知识又多又杂,自从接触java,就在一遍一遍捋脉络和深入学习。现在将这次的学习成果展示如下。 什么是多线程? 操作系统运行一个程序,就是一个线程。同时运行多个程序,就是多线程。即在同一时间࿰…...
【uniapp】微信小程序 , 海报轮播图弹窗,点击海报保存到本地,长按海报图片分享,收藏或保存
uivew 2.0 uniapp 海报画板 DCloud 插件市场 第一步,下载插件并导入HbuilderX 第二步,文件内 引入 海报组件 <template><painter ref"haibaorefs"></painter> <template> <script>import painter from /comp…...
SpringBoot与前端交互遇到的一些问题
一、XXX.jar中没有主清单属性 场景: SpringBoot打的jar包在Linux运行报错 解决方案: 百度找了很多都是一样的答案,但是解决不了我的问题,于是我新建了一个springboot项目发现打的jar包可以在Linux上运行。检查了下只要把下面这2个…...
Maven介绍与配置+IDEA集成Maven+使用Maven命令
目录 一、Maven简介 二、配置环境变量 三、IDEA集成Maven 1.配置本地仓库地址 2.集成Maven 3. pom.xml文件介绍 四、Maven命令 jar包太多、jar包相互依赖、不方便管理、项目编译还需要jar包,Maven工具来帮你! 一、Maven简介 Maven 是 Apache 软…...
毕业设计题目源码-毕业论文参考
目录 java语言ssm框架springboot框架微信小程序jspservletmysqljspservletsqlserverssh框架springmvc框架oracle无数据库 C#/asp/net语言B/S结构 浏览器模式C/S结构 窗体模式 安卓/androidapp 客户端appweb 客户端服务端 php语言php java语言 ssm框架 题目ssm828基于java的珠…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
AI语音助手的Python实现
引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...
