Digester XML解析教程


一、前置准备

1. 概念解释

XML 文件中有一个十分重要的概念 XPath,简单而言即用于定义元素的位置。

直接通过示例直观的了解 XPath 的定义,如一个 XML 的文件内容如下。那么值 Alex 对应的标签 name 其相应的 XPath 则为 student/name,即用于定位元素的路径。

<student>
    <id>1</id>
    <name>Alex</name>
</student>

2. 依赖导入

Apache digesterJava 中一种 XML 解析框架,能够在耗费最低系统资源的前提下实现高效 XML 文件解析。

使用方法简单快捷,在 Maven 项目中引入下述依赖即可开箱即用。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-digester3</artifactId>
    <version>3.2</version>
</dependency>

3. 文件数据

在开始之前先准备一个 XML 数据文件,文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<entityContainer>
    <entity>
        <id>1</id>
        <name>alex</name>
        <attribute>
            <key>math</key>
            <value>80</value>
        </attribute>
        <attribute>
            <key>science</key>
            <value>85</value>
        </attribute>
    </entity>
    <student>
        <id>2</id>
        <name>beth</name>
        <attribute>
            <key>math</key>
            <value>85</value>
        </attribute>
        <attribute>
            <key>science</key>
            <value>90</value>
        </attribute>
    </student>
</entityContainer>

二、基础操作

1. 实体对象

相对应于 XML 文件,需要创建对应的 Java 实体类用作载体。

同上述准备的 XML 文件内容此处创建了三个对应的实体类 EntityContainerEntityAttribute

@Data
public class EntityContainer {

    private List<Entity> entityList = new ArrayList<>();

    public void addEntity(Entity entity) {
        entityList.add(entity);
    }
}

@Data
public class Entity {

    private String id;

    private String name;

    private List<Attribute> attributes = new ArrayList<>();

    public void addAttribute(Attribute attribute) {
        attributes.add(attribute);
    }
}

@Data
public class Attribute {

    private String key;

    private String value;
}

2. 规则配置

完成上述步骤后即可开始正式的文件内容解析,Digester 解析工具其提供的规则配置方法参考下表:

方法 作用
addObjectCreate() 用于设置与 Java 对象的映射关系。
addSetProperties() 设置对象属性值。
addBeanPropertySetter() 用于 XPath 与实体对象属性的匹配
addSetNext() 一个标签内嵌套多个同标签时用于指定添加方法。

3. 读取示例

因此,之前提到的 XML 文件其对应的解析示例如下,其中 parse() 方法支持输入文件流与文本块的方式,前者基于事件驱动后者基于内存驱动,在解析大文件的情况下前者显然能够占用更少的资源。

public class DigestXmlTest {

    private final String location = "src\\main\\resources\\xml\\data.xml";

    @Test
    public void basicTest() {
        Digester digester = new Digester();
        digester.addObjectCreate("entityContainer", EntityContainer.class);
        digester.addSetProperties("entityContainer");

        digester.addObjectCreate("entityContainer/entity", Entity.class);
        // addBeanPropertySetter(): march xml label with bean field
        digester.addBeanPropertySetter("entityContainer/entity/id", "id");
        digester.addBeanPropertySetter("entityContainer/entity/name", "name");
        // addSetNext(): when march multiple item then put to collection
        digester.addSetNext("entityContainer/entity", "addEntity");

        digester.addObjectCreate("entityContainer/entity/attribute", Attribute.class);
        digester.addBeanPropertySetter("entityContainer/entity/attribute/key", "key");
        digester.addBeanPropertySetter("entityContainer/entity/attribute/value", "value");
        digester.addSetNext("entityContainer/entity/attribute", "addAttribute");

        try (FileReader reader = new FileReader(location)) {
            EntityContainer container = digester.parse(reader);
            System.out.println(container);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

三、进阶配置

1. 规则定义

在上述的示例中通过配置的规则即可实现数据文件的解析,但每次使用都需定义解析规则,显然过于繁杂。

因此,在 Digester 中提供了 AbstractRulesModule 可实现规则配置的统一管理,一次定义即可全局使用。

public class MyRuleModule extends AbstractRulesModule {
    @Override
    protected void configure() {
        forPattern("entityContainer").createObject().ofType(EntityContainer.class);

        forPattern("entityContainer/entity").createObject().ofType(Entity.class);
        forPattern("entityContainer/entity/id").setBeanProperty();
        forPattern("entityContainer/entity/name").setBeanProperty();
        forPattern("entityContainer/entity").setNext("addEntity");

        forPattern("entityContainer/entity/attribute").createObject().ofType(Attribute.class);
        forPattern("entityContainer/entity/attribute/key").setBeanProperty();
        forPattern("entityContainer/entity/attribute/value").setBeanProperty();
        forPattern("entityContainer/entity/attribute").setNext("addAttribute");
    }
}

2. 读取示例

上述配置方式相对应的读取示例代码如下:

public class DigestXmlTest {

    private final String location = "src\\main\\resources\\xml\\data.xml";

     @Test
    public void ruleTest() {
        // write once, use anywhere
        final DigesterLoader loader = DigesterLoader.newLoader(new MyRuleModule());
        final Digester digester = loader.newDigester();
        // read and parse
        try (FileReader reader = new FileReader(location)) {
            EntityContainer container = digester.parse(reader);
            System.out.println(container);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

四、注解方式

1. 注解介绍

除了代码配置解析规则之外,在 3.x 版本中提供了注解的方法,从而实现更便捷配置。

Digester 解析规则相配套的注解及其描述参考下表:

注解 描述
@ObjectCreate 等价于 addObjectCreate() 配置。
@BeanPropertySetter 等价于 addBeanPropertySetter() 配置。
@SetNext 等价于 addSetNext() 配置。

2. 对象定义

修改之前创建的实体类,通过注解方式配置 XPath 的值实现映射,对应代码如下:

@Data
@ObjectCreate(pattern = "entityContainer")
public class EntityContainer {

    private List<Entity> entityList = new ArrayList<>();

    @SetNext
    public void addEntity(Entity entity) {
        entityList.add(entity);
    }
}

@Data
@ObjectCreate(pattern = "entityContainer/entity")
public class Entity {

    @BeanPropertySetter(pattern = "entityContainer/entity/id")
    private String id;

    @BeanPropertySetter(pattern = "entityContainer/entity/name")
    private String name;
    

    private List<Attribute> attributes = new ArrayList<>();

    @SetNext
    public void addAttribute(Attribute attribute) {
        attributes.add(attribute);
    }
}

@Data
@ObjectCreate(pattern = "entityContainer/entity/attribute")
public class Attribute {

    @BeanPropertySetter(pattern = "entityContainer/entity/attribute/key")
    private String key;

    @BeanPropertySetter(pattern = "entityContainer/entity/attribute/value")
    private String value;
}

3. 规则定义

类似的通过继承 FromAnnotationsRuleModule 配置读取实体类的注解信息。

public class MyAnnotationModule extends FromAnnotationsRuleModule {
    @Override
    protected void configureRules() {
        bindRulesFrom(EntityContainer.class);
    }
}

4. 读取示例

上述步骤完成之后即可实现文件解析,同样是一次定义全局可用,对应的示例代码如下:

public class DigestXmlTest {

    private final String location = "src\\main\\resources\\xml\\data.xml";

    @Test
    public void annotationTest() {
        // write once, use anywhere
        final DigesterLoader loader = DigesterLoader.newLoader(new MyAnnotationModule());
        final Digester digester = loader.newDigester();
        // read and parse
        try (FileReader reader = new FileReader(location)) {
            EntityContainer container = digester.parse(reader);
            System.out.println(container);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

参考文章

  1. Apache Commons Digester 使用介绍

文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录