【DOM4J】Java中使用 DOM4J解析XML文件,解析Mybatis Mapper xml文件思路。

简述

在Java生态圈子里,解析xml的库不要太多,但大多数博客的作者们都推荐一个叫dom4j的库去做这个事情,它的功能最强大最全,也使用最多、当然也最复杂,在这里我因为碰上了需要解析Mybaits mapper xml文件的需求,故作此文以记录。

最终目的: 制作出解析XML的转换工具,使写满Mysql SQL语句的mapper文件,能够通过此工具转换成其他数据库SQL语法的xml文件

Dom4j的安装

dom4j官页:dom4j

dom4j-Api文档:Overview (dom4j 2.1.4 API)

Dom4j主要内容简述:

  • Document  文档 - 代表整个 XML 文档。 Document 对象通常称为 DOM 树。
  • Element  元素 - 表示 XML 元素。元素对象具有操作其子元素、文本、属性和命名空间的方法。
  • Attribute 属性 - 表示元素的属性。属性具有获取和设置属性值的方法。它具有父类型和属性类型。
  • Node 节点 - 表示元素、属性或处理指令。

常见的Dom4j方法描述:

  • SAXReader.read(xmlSource)() − 从 XML 源构建 DOM4J 文档。

  • Document.getRootElement() − 获取 XML 文档的根元素。

  • Element.node(index) −  获取元素中特定索引处的 XML 节点。

  • Element.attributes() −  获取元素的所有属性。

  • Node.valueOf(@Name) − 获取具有给定元素名称的属性值。

安装在项目中

maven:

<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.4</version>
</dependency>

gradle/KT:

implementation("org.dom4j:dom4j:2.1.4")

Dom4J的XML解析器

SAXReader解析器

接下来使用Dom4j的SAXReader创建解析器,在这里使用hutool.cn提供的工具类ResourceUtil去读取目录下的xml文件

SAXReader reader = new SAXReader();
String xmlString = ResourceUtil.readStr("conversion/TestMain.xml", StandardCharsets.UTF_8);
Document document = reader.read(new ByteArrayInputStream(xmlString.getBytes(StandardCharsets.UTF_8)));

三行代码分别代表:创建解析器 => 获取xml文件  =>  解析器解析XML内容

具体使用方法

获取

把整个XML文件解析成Dom4j的 Element对象

获取Root

Element root = document.getRootElement();

获取所有子元素

List<Element> childElements = root.elements()

 获取指定子元素-传入参数,参数为指定元素名

List<Element> childSelectElement = root.elements("select");

获取元素的各种内容

1. log.info("Name: {}", childElement.getName());
2. log.info("Text: {}", childElement.getText());
3. log.info("TextTrim: {}", childElement.getTextTrim());
4. log.info("XML: {}", childElement.asXML());
5. log.info("Data: {}", childElement.getData());
6. log.info("Parent: {}", childElement.getParent());
7. log.info("Path: {}", childElement.getPath());
8. log.info("UniquePath: {}", childElement.getUniquePath());

1.这一行获取并记录了当前元素的名称。例如,如果元素是 <Select>,它会输出 “Select”。

2.这一行获取并记录了当前元素的文本内容,包括子元素的文本和任何空白字符(如空格、换行符等)。

3.这一行获取并记录了当前元素的文本内容,但是去除了前后的空白字符。

4.这一行获取并记录了当前元素的XML字符串表示,包括其开始标签、属性、文本内容和结束标签。相当于获取了一个完整的XML

5.这一行获取并记录了当前元素的数据内容。这个方法通常用于获取元素的文本内容,但与getText()和getTextTrim()的区别可能依赖于具体的库实现。

6.这一行获取并记录了当前元素的父元素。如果当前元素是根元素,这个方法可能返回null。父元素和当前元素一样也是Element类型,方法与当前元素均相同

7.这一行获取并记录了当前元素的路径。这个路径是从根元素到当前元素的相对路径。

8.这一行获取并记录了当前元素在文档中的唯一路径。这个路径通常包括每个元素的索引,确保了即使是同名的兄弟元素,路径也是唯一的。

获取元素的属性

根据传入的参数获取元素属性的值
String elementId = childElement.attributeValue("id")
获取元素的属性对象 ,然后获取属性的文本和属性的值
Attribute attribute = childElement.attribute("attributeName");
if (attribute != null) {
    String attributeText = attribute.getText();
    String value = attribute.getValue();
}
获取元素的所有属性值,形成一个List
List<Attribute> attributes = childElement.attributes();
for (Attribute attribute : attributes) {
    String name = attribute.getName();
    String value = attribute.getValue();
    // 处理属性
}
检查元素是否具备这个属性 
boolean hasAttribute = childElement.hasAttribute("attributeName");

检查

检查子元素

selectSingleNode("subelementName"); 

检查属性

valueOf("@attributeName");

循环遍历元素中的子元素,检查子元素

Set<String> elementsToCheck = Set.of("select", "insert", "update", "delete");

for (Element childElement : childElements) {
       if (!elementsToCheck.contains(childElement.getName())) {
           log.error("未发现SQL语句元素");
       }
}

新增

在childElement中添加了一个<if></if>子元素

Element element = childElement.addElement("if");

在子元素中设置内容:<if> and studentId = 12312</if>

 element.setText(" and studentId = 12312");

为元素添加属性并设置值

thisElement.addAttribute("name", "sitinspring");

为一个元素添加一个子元素,并设置完属性和内容

supercarElement.addElement("carname")
      .addAttribute("type", "Ferrari 101")
      .addText("Ferrari 101");

修改

修改元素的某个属性的值

childElement.setAttribute("attributeName", "attributeValue");

修改元素的内容

在这里它会清除掉旧的内容,用新的内容填补

 elementToModify.setText("新的内容");

删除

删除元素中的某个子元素(连同它的一切都删除)

thisElement.remove(childElment);

删除元素的某个属性 

Attribute attribute=root.attribute("size"); 
root.remove(attribute);

解析Mybatis mapper xml的思路

不同场景,使用Dom4j解析的难度是不同的,越是解析复杂的XML内容,越是需要好的设计,Mybatis的mapper-xml 里面一般都写的是SQL,以及夹着一些XML,目的是实现动态加载参数以及动态拼接一些条件,那么解析起来就会有一些麻烦,以下提供一个思路,未必完美,但解决了一些Dom4j解析中会出现的问题

问题简述:

一个元素的内容文本中又包含着其他的元素,而其他的元素中又包含着内容,但彼此的内容是链接在一起的,例如Myabtis mapper-xml中语句都夹着xml,那该怎么办?

解决方法简述:

    <select id="queryDataScale" resultType="java.util.Map">
        select t.${idColumn} as id , t.${nameColumn} as name, case when t2.${idColumn} is null then 0 else 1 end as has_pms
        from ${tableName} t
        left join (
        select ${idColumn} from ${tableName} where ${idColumn} in
        <foreach collection="ids" open="(" close=")" item="item" separator="," >
            #{item}
        </foreach>
        and ${scaleSql}
        ) t2 on t.${idColumn} = t2.${idColumn}
        where t.${idColumn} in
        <foreach collection="ids" open="(" close=")" item="item" separator="," >
            #{item}
        </foreach>
    </select>

这里是一个Mybatis mapper-xml内容的一个案例,我们把它看成一个整体,其中select是整个内容的父级,无论它内容中的xml嵌套多少层,最外面都是它本身,所以我们需要一个循环,逐行获得它的节点内容

代码总结:

 Element root = document.getRootElement();
        List<Element> childElements = root.elements();

        for (Element childElement : childElements) {
           List<Element> thisElements = root.elements(childElement.getName());
            for (Element select : thisElements) {
                //这里相当于进入了Select标签元素内部
                List<Node> selectChildElements = select.content();
                for (Node child : selectChildElements) {
                    switch (child.getNodeType()) {
                        case Node.ELEMENT_NODE:
                            Element element = (Element) child;
                            log.info("Element: {}", element.getName());
                            break;
                        case Node.TEXT_NODE:
                            Text textNode = (Text) child;
                            log.info("Text: {}", textNode.getText());
                            break;
                        case Node.COMMENT_NODE:
                            Comment commentNode = (Comment) child;
                            // 添加注释
                            log.info("Comment: {}", commentNode.getText());
                            break;
                        default:
                            break;
                    }
                }
            }
        }

以上对整个XML做了一个遍历,第一个for循环是对xml总体做了一个遍历,而内部则是开始获取<select>的内容,这里通过  select.content() 获取了一个List<Node>,之所以拿到Node的集合,是因为这里我们不光要拿Element,而是对所有的内容,包括文本甚至注释也要拿到,然后遍历Node的集合,通过NodeType对内容做区分,最后可以通过Node.Text_NODE,按顺序逐行拿到所有的文本内容

写入新文件

创建OutputFormat对象,它用于格式化输出的内容,以下设置了缩进、换行和编码格式,具体更多format属性配置:OutputFormat (dom4j API)

最终通过XMLWriter ,将document(变量)的内容写入到了指定的文件中

OutputFormat format = OutputFormat.createPrettyPrint();
format.setExpandEmptyElements(true);
format.setIndent(true);
format.setIndentSize(4);
format.setLineSeparator("\n");
format.setNewlines(true);
format.setEncoding("UTF-8");
format.isXHTML();
// 创建XMLWriter对象,将文档写入文件
XMLWriter writer = new XMLWriter(new FileWriter(new File("src/main/resources/result/text.xml")).getOutputStream(), format);
        writer.write(document);
        writer.close();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值