Jacoco XML 解析

news2024/10/7 8:30:44

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;

    /**
     * 开始解析元素时触发
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) {
        switch (qName) {
            // 跳过 sessioninfo 和 line
            case SESSIONINFO:
            case LINE:
                return;
            // 解析report
            case REPORT:
                report = new Report();
                report.setName(attributes.getValue(REPORT_NAME));
                break;
            // 解析package
            case PACKAGE:
                currentPackage = new Package();
                currentPackage.setName(attributes.getValue(PACKAGE_NAME));
                if (report != null) {
                    report.addPackage(currentPackage);
                }
                break;
            // 解析class
            case CLASS:
                currentClass = new Clazz();
                currentClass.setName(attributes.getValue(CLASS_NAME));
                currentClass.setSourcefilename(attributes.getValue(CLASS_SOURCEFILENAME));
                if (currentPackage != null) {
                    currentPackage.addClass(currentClass);
                }
                break;
            // 解析method
            case METHOD:
                currentMethod = new Method();
                currentMethod.setName(attributes.getValue(METHOD_NAME));
                currentMethod.setDesc(attributes.getValue(METHOD_DESC));
                if (currentClass != null) {
                    currentClass.addMethod(currentMethod);
                }
                break;
            // 解析sourcefile
            case SOURCEFILE:
                sourcefile = attributes.getValue(SOURCEFILE_NAME);
                break;
            // 解析counter
            case 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;
        }

    }


    /**
     * 结束解析元素时触发
     */
    @Override
    public void endElement(String uri, String localName, String qName) {
        switch (qName) {
            case SESSIONINFO:
            case LINE:
                // 跳过 sessioninfo 和 line
                return;
            case SOURCEFILE:
                // 避免重复加入 counter 到 page
                sourcefile = 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 属于 method
        if (currentMethod != null) {
            currentMethod.addCounter(currentCounter);
        }
        // counter 属于 class
        else if (currentClass != null) {
            currentClass.addCounter(currentCounter);
        }
        // counter 属于 package
        else if (currentPackage != null) {
            // 跳过sourcefile里的counter 避免重复加入
            if (sourcefile == null) {
                currentPackage.addCounter(currentCounter);
            }
        }
        // counter 属于 report
        else 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 ==========================================");
                }
            }
        }

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/916149.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决OpenFOAM颗粒计算输出文件Paraview无法打开问题(一)

长话短说&#xff0c;关于这个问题&#xff0c;有两个解决方案&#xff0c;一是将文件类型转换为VTK格式&#xff0c;二是采用github上的脚本&#xff0c;将OpenFOAM输出的位置文件转换成真实的颗粒的真实位置文件。 1. 转换为VTK格式 在OF中&#xff0c;将输出结果转变为VTK…

自动化测试平台seldom-platform部署及使用

介绍 seldom-platform是一个基于seldom测试框架的测试平台 项目地址&#xff1a;https://github.com/SeldomQA 文档&#xff1a;seldom 语雀 首先&#xff0c;专门为seldom测试框架提供平台化支持。其次&#xff0c;只负责自动化测试项目的解析、执行用例&#xff0c;当然…

HTML-常见标签、HTML5新特性

HTML 软件架构 1.C/S架构 (1) C/S架构即Client/Server&#xff08;客户机/服务器&#xff09;结构。 (2) C/S 架构特点 ​ C/S结构在技术上很成熟&#xff0c;它的主要特点是交互性强、具有安全的存取模式、网络通信量低、响应速度快、利于处理大量数据。但是该结构的程序是…

发布完体验版以后,出现接口调用失败,但是在本地开发环境中可以正常访问的情况,解决办法

1.一般解决办法 通过打开微信小程序调试模式 进行调用&#xff0c;但这会有些许问题&#xff0c;出现vConsole按钮**&#xff0c;影响美观** 如图所示&#xff1a; 1.1.进入小程序 点击右上角的3个点儿 1.2.点击开发调试 1.3.点击打开调试 1.4.显示出vconsole按钮&#xf…

皕杰报表(BIOS Report)中设置序号的方法之一

皕杰报表软件是一款应用广泛的Java报表工具&#xff0c;使用简单、制表效率高&#xff0c;可以轻松制作出各种各样的报表&#xff0c;是一个不错的Web报表软件。在报表设计过程中&#xff0c;经常要用到各种序号&#xff0c;那么该如何实现这样的报表呢&#xff1f;下面根据需求…

taro h5 formData上传图片的坑-Required request part ‘file‘ is not present

描述&#xff1a;用formData上传图片 1、生成formData const formData new FormData() formData.append(file, data) // data是file formData.append(xxx, xxx) // 添加其他参数2、用taro.request请求 Taro.request({url: xxxx,data: formData,header: {Content-Type: mult…

LeetCode--HOT100题(39)

目录 题目描述&#xff1a;101. 对称二叉树&#xff08;简单&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;101. 对称二叉树&#xff08;简单&#xff09; 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 LeetCode做题链接&#xff1a;LeetCode-…

B. Fancy Coins - 思维

分析&#xff1a; 赛事思路总是wa&#xff0c;看完题解发现思维要打开&#xff0c;思维还是要练啊。换个思路就会清晰很多&#xff0c;可以根据m 和 k先统计出需要多少k面值的和多少1面值的&#xff0c;然后分别对两种面值不够的补神奇的硬币&#xff0c;这样可能会导致不是最小…

前端处理图片文件的方法

在项目开发过程中&#xff0c;有一个需求&#xff0c;需要前端对上传的图片进行处理&#xff0c;以字符串的形式传给后端&#xff0c;实现效果如下&#xff1a; 1.上传图片的组件 在该项目中&#xff0c;使用了element plus组件库 <el-uploadv-model:file-list"fileL…

根据源码,模拟实现 RabbitMQ - 网络通讯设计,自定义应用层协议,实现 BrokerServer (8)

目录 一、网络通讯协议设计 1.1、交互模型 1.2、自定义应用层协议 1.2.1、请求和响应格式约定 ​编辑 1.2.2、参数说明 1.2.3、具体例子 1.2.4、特殊栗子 1.3、实现 BrokerServer 1.3.1、属性和构造 1.3.2、启动 BrokerServer 1.3.3、停止 BrokerServer 1.3.4、处…

android framework-Pixel3真机制作开机动画实战

第一步、制作bootanimation.zip 1.1、图片格式 推荐使用jpg或者png格式的图片 1.2、图片命名规则 多张图片时&#xff0c;Android显示logo是按照图片名称数值的大小顺序来显示的&#xff1b;图片命名需要注意名称后面要以数字结尾&#xff0c;并且按图片总张数的位数来补齐…

行为型(五) - 迭代器模式

一、概念 迭代器模式&#xff08;Iterator Pattern&#xff09;&#xff1a;迭代器模式将集合对象的遍历操作从集合类中拆分出来&#xff0c;放到迭代器类中&#xff0c;让两者的职责更加单一。 通俗的讲&#xff1a;迭代器模式就是提供一种遍历的方法&#xff0c;这种方法有…

卡马A1/B1和VEAZEN费森S88怎么样?有什么优缺点?综合对比评测哪一款更适合初学者/进阶者购买?

每个吉他爱好者应该都想拥有一把全单吉他&#xff0c;毕竟全实木的民谣吉他会有更好的声音爆发力和更细腻的音色&#xff0c;在入门和进阶全单吉他中&#xff0c;经常收到私信询问这两款VEAZEN费森S88系列和KEPMA卡马A1系列这两款全单吉他怎么样&#xff1f; 卡马A1/B1和VEAZE…

【LangChain系列 1】 LangChain初探

原文链接&#xff1a;【LangChain系列 1】LangChain初探https://mp.weixin.qq.com/s/9UpbM84LlsHOaMS7cbRfeQ 本文速读&#xff1a; LangChain是什么 LangChain初探 环境准备 LLMs Prompt Templates Output Parser 第一个LLMChain应用 01 LangChain是什么 LangChain是一…

Linux socket网络编程

一、主机字节序列和网络字节序列 主机字节序列分为大端字节序列和小端字节序列&#xff0c;不同的主机采用的字节序列可能不同。大端字节序列是指一个整数的高位字节存储在内存的低地址处&#xff0c;低位字节存储在内存的高地址处。小端字节序列是指整数的高位字节存储在内存…

Prometheus+Grafana+AlertManager监控Linux主机状态

文章目录 PrometheusGrafanaAlertManager监控平台搭建开始监控Grafana连接Prometheus数据源导入Grafana模板监控Linux主机状态 同系列文章 PrometheusGrafanaAlertManager监控平台搭建 Docker搭建并配置Prometheus Docker拉取并配置Grafana Docker安装并配置Node-Exporter …

ChatGPT取代人类仍然是空想?有没有一种可能是AI在迷惑人类

ChatGPT自从去年发布以来&#xff0c;就掀起了这些大语言模型将如何颠覆一切的激烈讨论&#xff0c;从为学生写作文、输出SEO文章&#xff0c;甚至取代谷歌成为世界上最受欢迎的搜索引擎&#xff0c;影响领域无所不包&#xff0c;甚至可能取代编剧、小说家和音乐家等从事创意工…

spring复习:(57)PropertyOverrideConfigurer用法及工作原理

一、属性配置文件 dataSource.urljdbc:mysql://xxx.xxx.xxx.xxx/test dataSource.usernameroot dataSource.passwordxxxxxx dataSource.driverClassNamecom.mysql.jdbc.Driver #dataSource.typecom.alibaba.druid.pool.DruidDataSource二、spring配置文件 <?xml version&…

C++STL之vector 容器

食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f340;本文前置知识&#xff1a;C基础 ♈️今日夜电波&#xff1a;恋 —星野源 0:13 ━━━━━━️&#x1f49f;──────── 4:13 &…

亚马逊自动下单软件是怎么操作的?

如果需要亚马逊自动下单软件&#xff0c;那么首选肯定是亚马逊鲲鹏系统&#xff0c;亚马逊鲲鹏系统是一款模拟真人进行全自动化操作的软件&#xff0c;可以注册亚马逊买家号、养号、自动下单留评等&#xff0c;功能非常的齐全。 要进行下单&#xff0c;那么首先我们就需要有一批…