当前位置: 首页 > 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的珠…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...