Jacoco XML 解析

这篇具有很好参考价值的文章主要介绍了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可以逐行处理标签
Jacoco XML 解析,java开发,xml,html
Jacoco XML 解析,java开发,xml,html
Jacoco XML 解析,java开发,xml,html
Jacoco XML 解析,java开发,xml,html文章来源地址https://www.toymoban.com/news/detail-667915.html





/**
 * @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 ==========================================");
                }
            }
        }

    }
}

到了这里,关于Jacoco XML 解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java中解析XML文件

            XML(EXtensible Markup Language),可扩展标记语言,相比于HTML可以自定义标签,不遵循W3C标准         XML特点:跨平台、跨语言、跨系统。XML与操作系统、编程语言的开发平台无关。         XML作用:①数据交互②使用XML文件配置应用程序和网站 1.文档声明 包括文档

    2024年02月09日
    浏览(52)
  • 【实用 Python 库】使用 XPath 与 lxml 模块在 Python 中高效解析 XML 与 HTML

    在今天的信息时代,数据无处不在,从网络爬虫到数据挖掘,从文本处理到数据分析,我们时常需要从结构化文档中提取有用的信息。XPath 是一门强大的查询语言,它可以在 XML 与 HTML 等文档中定位特定的元素与数据。而在 Python 中, lxml 模块为我们提供了一种高效解析 XML 与

    2024年02月10日
    浏览(43)
  • C++QT开发——Xml、Json解析

    可扩展标记语言,标准通用标记语言(Extensible Markup Language)的子集,简称XML,是一种定义电子文档结构和描述其内容的国际标准语言,被设计用来传输和存储数据。 可扩展标记语言与Access],Oracle]和SQL Server等数据库不同,数据库提供了更强有力的数据存储和分析能力,例如:

    2024年02月06日
    浏览(47)
  • java使用DOM4j解析XML文件

    dom4j是java中的XML API,性能优异、功能强大、开放源代码。 也是所有解析XML文件方法中最常用的! 准备需要j的ar包添加到lib目录下,dom4j-2.1.1.jar(网上随便找一个) 新建项目,在项目下创建info.xml  创建类Test,再使用DOM4j实现对XML文件增删改查 1、获取Document对象 2、展示所有的手机

    2024年02月09日
    浏览(45)
  • [Java学习日记]日志、类加载器、XML、DTD与schema、XML解析、XPath、单元测试、Assert、BeforeAfter、注解、自定义注解、注解案例

    下面的案例中只是做一个简单的了解,不做深入理解啦 目录 一.使用Logback写日志 二.类加载器 三.XML 四.XML编写规定:DTD与schema 五.XML解析 六.XPath 七.单元测试 七.Assert(断言):检验方法结果 八.使用before与after注解备份与还原数据 九.注解 十.自定义注解 十一.注解案例:用注

    2024年02月04日
    浏览(57)
  • java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

    正则表达式验证网站 1、 ? :表示前边这个字符可以出现0次或者1次。例如下边 /used? 既可以匹配 use 也可以匹配 used 。 2、 * :匹配0个或者多个字符, * 号代表前边这个字符可以出现0次或者多次。例如 /ab*c 可以匹配 ac、abc、abbbbc 3、 + :与 * 号不同的是, + 需要前面这个字符

    2024年02月06日
    浏览(53)
  • 【196】JAVA8 用 DOM 方式的 API 解析XML文件,无需引入第三方依赖。

    JAVA8 可以使用标准的 DOM 方式的 API 来编写代码解析 XML 文件,无需引入第三方依赖。下面是两个测试用的 XML 文件: test.xml test2.xml 负责接收所有信息的 AllDataDTO.java 负责接收用户信息的 AllDataUserDTO.java 处理 XML 文件的工具类,XmlUtils.java 用于测试效果的类 Main.java 输出结果:

    2024年01月22日
    浏览(41)
  • HarmonyOS鸿蒙基于Java开发:Java UI 常用组件 组件通用XML属性

    目录 基础XML属性 间距相关的XML属性 滚动条相关的XML属性 旋转缩放相关的XML属性 焦点相关的XML属性 Component是所有组件的基类,Component支持的XML属性,其他组件都支持。 Component支持的XML属性如下表。 表1  基础属性的相关说明 属性名称

    2024年01月25日
    浏览(62)
  • xml系列篇之xml解析

    接下来看看由辉辉所写的关于xml的相关操作吧 目录 🥳🥳Welcome Huihui\\\'s Code World ! !🥳🥳 是什么 为什么要使用 优点 💡辉辉小贴士:xml在数据库辅助类中的应用 💡辉辉小贴士:怎么获取不同位置下的配置文件呢? 怎么使用 1.DOM4J 代码示例 2. XPath 代码示例 3. SAX 4. StAX 5. JAXB

    2024年02月10日
    浏览(61)
  • .xml是什么文件 html的区别是什么 ?xml用什么编辑器?

    XML 在许多领域中都有广泛的应用。以下是一些常见的用途: 数据交换:XML 可以用作数据交换的标准格式。许多应用程序和系统使用 XML 来传输和共享数据,因为 XML 的结构化特性和可读性使得数据解析和处理更加方便。 配置文件:许多软件和系统使用 XML 文件来存储配置信息

    2024年01月21日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包