当前位置: 首页 > news >正文

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解析器&#xff1a; ○ 优点&#xff1a;易于使用&#xff0c;提供完整的文档树&#xff0c;可以方便地修改和遍历XML文档。 ○ 缺点&#xff1a;对大型文档消耗内存较多&#xff0c;加载整个文档可能会变慢。 ○ 适用场景&#xff1a;适合小型XML文档…...

【面试题】JDK(工具包)、JRE(运行环境和基础库)、JVM(java虚拟机)之间的关系?

【面试题】JDK、JRE、JVM之间的关系&#xff1f; JDK(Java Development Kit):Java开发工具包&#xff0c;提供给Java程序员使用&#xff0c;包含了JRE&#xff0c;同时还包含了编译器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)程序控制&#xff08;查询&#xff09;方式&…...

Windows下MATLAB调用Python函数操作说明

MATLAB与Python版本的兼容 具体可参看MATLAB与Python版本的兼容 操作说明 操作说明请参看下面两个链接&#xff1a; 操作指南 简单说明&#xff1a; 我安装的是MATLAB2022a和Python3.8.6&#xff08;安装时请勾选所有可以勾选的&#xff0c;包括路径&#xff09;。对应版本安…...

【android12-linux-5.1】【ST芯片】驱动与HAL移植后数据方向异常

ST的传感器驱动与HAL一直成功后&#xff0c;能拿到数据了&#xff0c;但是设备是横屏&#xff0c;系统默认是竖屏。就会出现屏幕自动转动时方向是错的的情况&#xff0c;设备横立展示的是竖屏&#xff0c;设备竖立展示的是横屏。 这个是PCB上设计的传感器贴片方向和横屏不一致…...

JavaScript Es6_3笔记

JavaScript 进阶 文章目录 JavaScript 进阶编程思想面向过程面向对象 构造函数原型对象constructor 属性对象原型原型继承原型链 了解构造函数原型对象的语法特征&#xff0c;掌握 JavaScript 中面向对象编程的实现方式&#xff0c;基于面向对象编程思想实现 DOM 操作的封装。 …...

Qt产生随机数

Qt产生随机数 提问&#xff1a;注意&#xff1a;实现&#xff1a; 提问&#xff1a; 有没有小伙伴遇到这么一种情况&#xff0c;使用rand()和qrand()函数生成的随机数好像不是那么随机&#xff0c;每次都一样。那这种就叫做“伪随机”&#xff0c;因为没有种随机数种子&#x…...

postgresql常用函数-数学函数

postgresql常用函数 简介数学函数算术运算符绝对值取整函数乘方与开方指数与对数整数商和余数弧度与角度常量 π符号函数生成随机数 简介 函数&#xff08;function&#xff09;是一些预定义好的代码模块&#xff0c;可以将输入进行计算和处理&#xff0c;最终输出一个 结果值…...

【排序】快速排序(前后指针法)—— 考的最少的一种算法

以从小到大的顺序进行说明。 前后指针法 是指对于一个数组&#xff0c;定义前后各一个指针&#xff08;prev 和 cur&#xff09; prev用于卡一个比基准值大的值进行交换cur用于向前遍历出比基准值小的&#xff0c;和prev进行交换 图解 初始化 选出基准值4 如果cur 所在的值…...

软考:中级软件设计师:关系代数:中级软件设计师:关系代数,规范化理论函数依赖,它的价值和用途,键,范式,模式分解

软考&#xff1a;中级软件设计师:关系代数 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是需要细心准备的 &…...

openCV实战-系列教程2:阈值与平滑处理(图像阈值/图像平滑处理/高斯/中值滤波)、源码解读

1、图像阈值 t图像阈值函数&#xff0c;就是需要判断一下像素值大于一个数应该怎么处理&#xff0c;小于一个数应该怎么处理 ret, dst cv2.threshold(src, thresh, maxval, type) 参数解析&#xff1a; src&#xff1a; 原始输入图&#xff0c;只能输入单通道图像&#…...

C语言第五章-循环结构练习

1、设计一个小型模拟彩票中奖机&#xff0c;已知彩票中奖号码是一个固定的3位数&#xff08;原始号码&#xff09;。对任意一个3位数&#xff0c;取出它的每位数字和原始号码的每位数字比较&#xff0c;有1位数相同中三等奖&#xff0c;有2位数相同中二等奖&#xff0c;有3位数…...

Echarts面积图2.0(范围绘制)

代码&#xff1a; // 以下代码可以直接粘贴在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模式的区别

背景&#xff1a; flink在开启checkpoint的时候有两种模式可以选择&#xff0c;exact-one和atleastone模式&#xff0c;那么这两种模式有什么区别呢&#xff1f; exact-one和atleastone模式的区别 先说结论&#xff1a;exact-one可以完全做到状态的一致性&#xff0c;而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…...

用大白话来讲讲多线程的知识架构

感觉多线程的知识又多又杂&#xff0c;自从接触java&#xff0c;就在一遍一遍捋脉络和深入学习。现在将这次的学习成果展示如下。 什么是多线程&#xff1f; 操作系统运行一个程序&#xff0c;就是一个线程。同时运行多个程序&#xff0c;就是多线程。即在同一时间&#xff0…...

【uniapp】微信小程序 , 海报轮播图弹窗,点击海报保存到本地,长按海报图片分享,收藏或保存

uivew 2.0 uniapp 海报画板 DCloud 插件市场 第一步&#xff0c;下载插件并导入HbuilderX 第二步&#xff0c;文件内 引入 海报组件 <template><painter ref"haibaorefs"></painter> <template> <script>import painter from /comp…...

SpringBoot与前端交互遇到的一些问题

一、XXX.jar中没有主清单属性 场景&#xff1a; SpringBoot打的jar包在Linux运行报错 解决方案&#xff1a; 百度找了很多都是一样的答案&#xff0c;但是解决不了我的问题&#xff0c;于是我新建了一个springboot项目发现打的jar包可以在Linux上运行。检查了下只要把下面这2个…...

Maven介绍与配置+IDEA集成Maven+使用Maven命令

目录 一、Maven简介 二、配置环境变量 三、IDEA集成Maven 1.配置本地仓库地址 2.集成Maven 3. pom.xml文件介绍 四、Maven命令 jar包太多、jar包相互依赖、不方便管理、项目编译还需要jar包&#xff0c;Maven工具来帮你&#xff01; 一、Maven简介 Maven 是 Apache 软…...

毕业设计题目源码-毕业论文参考

目录 java语言ssm框架springboot框架微信小程序jspservletmysqljspservletsqlserverssh框架springmvc框架oracle无数据库 C#/asp/net语言B/S结构 浏览器模式C/S结构 窗体模式 安卓/androidapp 客户端appweb 客户端服务端 php语言php java语言 ssm框架 题目ssm828基于java的珠…...

如何在Windows系统上完整启用MacBook Pro Touch Bar显示功能

如何在Windows系统上完整启用MacBook Pro Touch Bar显示功能 【免费下载链接】DFRDisplayKm Windows infrastructure support for Apple DFR (Touch Bar) 项目地址: https://gitcode.com/gh_mirrors/df/DFRDisplayKm 还在为MacBook Pro在Windows系统中Touch Bar只能用作…...

智能停车系统告别拥堵!巨有科技让景区停车畅行无忧

每逢节假日&#xff0c;景区停车场便成了“重灾区”——入口大排长龙、场内找位半小时、缴费排队苦不堪言。这不仅严重消耗游客耐心&#xff0c;更直接拉低景区口碑与运营效率。在文旅消费持续回暖的今天&#xff0c;停车体验已成为衡量景区服务力的关键指标。巨有科技以数据驱…...

C++智能指针与内存管理实践

C智能指针与内存管理实践智能指针是C中自动管理动态内存的关键工具。通过RAII机制&#xff0c;智能指针在对象生命周期结束时自动释放内存&#xff0c;避免内存泄漏和悬空指针问题。std::unique_ptr提供独占所有权语义&#xff0c;确保同一时刻只有一个指针拥有资源。它的开销极…...

5分钟搞定百度网盘限速:baidu-wangpan-parse全功能指南

5分钟搞定百度网盘限速&#xff1a;baidu-wangpan-parse全功能指南 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 还在为百度网盘的限速而烦恼吗&#xff1f;想下载大文件却只…...

瀚高企业版V9.1.1在pg_restore还原备份文件时提示extract函数语法问题

文章目录环境症状问题原因解决方案环境 系统平台&#xff1a;UOS&#xff08;海光&#xff09; 版本&#xff1a;9.0 症状 瀚高数据库版本&#xff1a;企业版 V9.1.1 症状&#xff1a; pg_restore还原备份文件时&#xff0c;提示下面错误 pg_restore: 来自 TOC 记录 259; …...

战略视角:如何用AI自动化重构团队工作流

战略视角&#xff1a;如何用AI自动化重构团队工作流 【免费下载链接】midscene AI-powered, vision-driven UI automation for every platform. 项目地址: https://gitcode.com/GitHub_Trending/mid/midscene 在数字化加速的时代&#xff0c;企业面临的核心挑战不再是技…...

GEO优化的两大误区:你是在“交学费”还是在“抢红利”?

当AI搜索成为用户的新入口&#xff0c;一批先行者已经吃到了红利。但更多人&#xff0c;还在两个极端之间摇摆。 你有没有遇到过这样的情况&#xff1f; 刷到某个同行&#xff0c;因为上了DeepSeek或豆包的推荐&#xff0c;咨询量翻了几倍。你也心动&#xff0c;开始研究GEO&a…...

Claude Code Unpacked:终端里的AI编程革命,一图胜千言

Claude Code Unpacked&#xff1a;终端里的AI编程革命&#xff0c;一图胜千言 还记得那个在Hacker News上一夜之间收获480票的项目吗&#xff1f;当开发者们第一次看到Claude Code在终端中流畅地理解代码、自动重构、甚至主动提出优化建议时&#xff0c;整个社区都沸腾了。这不…...

vue3+python基于Django的校园二手物品交易系统设计与实现49895951

目录同行可拿货,招校园代理 ,本人源头供货商项目背景技术栈核心功能模块关键实现细节扩展性设计参考开源项目项目技术支持源码获取详细视频演示 &#xff1a;同行可合作点击我获取源码->->进我个人主页-->获取博主联系方式同行可拿货,招校园代理 ,本人源头供货商 项目…...

Wedding-website图片库与相册功能:展示精美婚礼照片的最佳实践

Wedding-website图片库与相册功能&#xff1a;展示精美婚礼照片的最佳实践 【免费下载链接】wedding-website Our Wedding Website &#x1f46b; 项目地址: https://gitcode.com/gh_mirrors/we/wedding-website 婚礼是人生中最珍贵的时刻之一&#xff0c;而如何将这些美…...