解析 xml,你可以采用 Java 原生支持的 sax、DOM 或者第三方的 dom4j 等。虽然提供了各式各样的解析方式,但是解析一个复杂的 xml 所编写的 Java 代码是非常麻烦的。
而 Apache 的 commons 项目中 Digester 项目解决了这个问题,它可以很轻易地将xml文件解析成Java对象,让你直接去使用,而你仅仅需要去预定义一份解析规则,Digester 的内部采用 SAX 来解析 XML 文件。
对于解析规则,可以采用如下三种方式
- Java的方式
- xml的方式
- annotation的方式
常用的规则:
- ObjectCreate,创建对象实例。
- SetProperties,将标签属性(Attribute)与要创建的对象的属性相关联。
- BeanPropertySetter,将标签所包含标签与要创建的对象的属性相关联。
- SetNext,设置遇到下一个标签时的动作。
一、解析前的准备工作
1、创建一个 springboot 项目
引入如下两个依赖,主要是 digester 的依赖,lombok 用来简化实体中的 get/set
<!-- digester 解析 xml -->
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-digester3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-digester3</artifactId>
<version>3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2、准备好两种格式的 xml 文件
1、第一种,标签中包含属性的 xml
<?xml version="1.0" encoding="UTF-8"?>
<school>
<class code="001" Title="一年一班">
<teacher name="张三"/>
<student code="01" name="小明"/>
<student code="02" name="小红"/>
</class>
<class code="002" Title="一年二班">
<teacher name="李四"/>
<student code="01" name="小王"/>
</class>
</school>
2、第二种,标签和属性分离的
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<title>西游记</title>
<description>西天取经的故事</description>
<person>孙悟空</person>
<person>猪八戒</person>
</book>
<book>
<title>三国演义</title>
<description>历史故事</description>
<person>诸葛亮</person>
<person>刘备</person>
</book>
</books>
二、开始解析
1、解析 xml 标签中包含属性的 school.xml
1、创建相关 java 实体对象,用来接收解析的 xml
/**
* @author wuxiongwei
* @date 2021/2/23 14:08
* @Description
*/
@Data
@ObjectCreate(pattern = "school")
public class SchoolGroup {
private List<Class> list = new ArrayList();
@SetNext
public void addNext(Class clazz) {
this.list.add(clazz);
}
}
/**
* @author wuxiongwei
* @date 2021/2/23 14:07
* @Description
*/
@Data
//当碰到 xml 中的 school/class 标签时,创建一个对象
@ObjectCreate(pattern = "school/class")
public class Class {
/**
* SetProperty 适用于接收如下格式
* <class code="001" Title="一年一班">
*/
@SetProperty(pattern = "school/class")
private String code;
/**
* <class code="001" Title="一年一班">
* 如果 xml 中标签的属性名称与对象属性名称不一致,可以使用 attributeName ,标识当前这个对象属性 title 接收 xml 标签的 Title 属性
*/
@SetProperty(pattern = "school/class",attributeName = "Title")
private String title;
private Teacher teacher = new Teacher();
private List<Student> studentList = new ArrayList<>();
// 当碰到 school/class 标签下的 student 标签的处理
@SetNext
public void addNext(Student student) {
this.studentList.add(student);
}
// 当碰到 school/class 标签下的 teacher 标签的处理
@SetNext
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
/**
* @author wuxiongwei
* @date 2021/2/23 14:10
* @Description
*/
@Data
@ObjectCreate(pattern = "school/class/teacher")
public class Teacher {
@SetProperty(pattern = "school/class/teacher")
private String name;
}
/**
* @author wuxiongwei
* @date 2021/2/23 14:10
* @Description
*/
@Data
@ObjectCreate(pattern = "school/class/student")
public class Student {
@SetProperty(pattern = "school/class/student")
private String code;
@SetProperty(pattern = "school/class/student")
private String name;
}
2、关键解析类
/**
* @author wuxiongwei
* @date 2021/2/23 14:21
* @Description
*/
public class SchoolDigester {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static void main(String[] args) {
// 定义要解析的 XML 的路径
File file = null;
try {
file = ResourceUtils.getFile("classpath:school.xml");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Digester digester = DigesterLoader.newLoader(new FromAnnotationsRuleModule() {
@Override
protected void configureRules() {
// 这里只添加HolidayInfoXmlGroup即可
bindRulesFrom(SchoolGroup.class);
}
}).newDigester();
try {
SchoolGroup parse = digester.parse(file);
for (Class aClass : parse.getList()) {
LOGGER.info("学校xml解析信息如下"+aClass.toString());
}
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
LOGGER.info("load error:",e.getMessage());
}
}
}
3、解析数据
2、解析 xml 标签和属性分离的 book.xml
同样,先把接收的对象创建
1、创建接收 xml 的实体对象
/**
* @author wuxiongwei
* @date 2021/2/23 14:08
* @Description
*/
@Data
@ObjectCreate(pattern = "books")
public class BookGroup {
private List<Book> list = new ArrayList();
@SetNext
public void addNext(Book clazz) {
this.list.add(clazz);
}
}
/**
* @author wuxiongwei
* @date 2021/2/23 14:07
* @Description
*/
@Data
@ObjectCreate(pattern = "books/book")
public class Book {
/**
* 适用于如下这种 xml 标签格式
* <book>
* <title>西游记</title>
* </book>
*/
@BeanPropertySetter(pattern = "books/book/title")
private String title;
@BeanPropertySetter(pattern = "books/book/description")
private String description;
private List<Person> personList = new ArrayList<>();
@SetNext
public void addNext(Person clazz) {
this.personList.add(clazz);
}
}
/**
* @author wuxiongwei
* @date 2021/2/23 14:34
* @Description
*/
@Data
@ObjectCreate(pattern = "books/book/person")
public class Person {
@BeanPropertySetter(pattern = "books/book/person")
private String name;
}
2、关键解析类
/**
* @author wuxiongwei
* @date 2021/2/23 14:21
* @Description
*/
public class BookDigester {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static void main(String[] args) {
// 定义要解析的 XML 的路径
File file = null;
try {
file = ResourceUtils.getFile("classpath:book.xml");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Digester digester = DigesterLoader.newLoader(new FromAnnotationsRuleModule() {
@Override
protected void configureRules() {
// 这里只添加HolidayInfoXmlGroup即可
bindRulesFrom(BookGroup.class);
}
}).newDigester();
try {
BookGroup parse = digester.parse(file);
for (Book aClass : parse.getList()) {
LOGGER.info("书籍xml解析信息如下"+aClass.toString());
}
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
LOGGER.info("load error:",e.getMessage());
}
}
}