前言
JDK 中内置了 dom 和 sax 两种解析 xml 文件的方式,两者各有利弊,根据场景选用
一、dom和sax的区别
dom方式的特点:
- 树型解析器,将读入的xml文档转换成树结构对象[Document]
- 基于内存的,不管文件有多大,都会将所有的内容预先装载到内存中
- 可以读取xml文件内容,也可以向xml文件中插入数据,修改数据
sax方式的特点:
- 流机制解析器,在读入xml文档时生成相应的事件
- 基于事件的,当某个事件被触发时,才获取相应的xml的部分数据,占用内存较小
- 按顺序一边读取数据,一边进行解析,在读取数据的时候会触发事件,每触发一次,就执行一次触发方法
- 只能对xml文件内容进行读取,而不能在文件中插入或修改数据
二、解析xml
1. 准备
要解析的 my.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<people>
<students>
<student id="1">
<name>张三</name>
<age>23</age>
</student>
<student id="2">
<name>李四</name>
<age>24</age>
</student>
</students>
<teachers>
<teacher id="3">
<name>王五</name>
<age>25</age>
</teacher>
</teachers>
</people>
2. dom方式
将 xml 文件构造出 Document 树状对象,针对 Document 就可以获取并修改数据
public class DomTest {
public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException, TransformerException {
// 读取xml文件
String path = DomTest.class.getResource("").getPath();
File f = new File(path + "/my.xml");
// 1.创建一个DOM解析器工厂
DocumentBuilderFactory factory =DocumentBuilderFactory.newInstance();
// 2.通过工厂生产一个解析器对象
DocumentBuilder builder = factory.newDocumentBuilder();
// 3.将 xml 文件封装到 Document 对象
Document dom = builder.parse(f);
// 获取数据
NodeList studentList = dom.getElementsByTagName("student");
for (int i = 0; i < studentList.getLength(); i++) {
Element student = (Element) studentList.item(i);
String id = student.getAttribute("id");
System.out.println(student.getTagName() + ":" + id);
NodeList names = student.getElementsByTagName("name");
NodeList ages = student.getElementsByTagName("age");
System.out.println(names.item(0).getNodeName() + ":" + names.item(0).getTextContent());
System.out.println(ages.item(0).getNodeName() + ":" + ages.item(0).getTextContent());
System.out.println("=========================");
}
// 写入数据
NodeList teacherList = dom.getElementsByTagName("teacher");
for (int i = 0; i < teacherList.getLength(); i++) {
Element teacher = (Element) studentList.item(i);
Element sex = dom.createElement("sex");
sex.setTextContent("男");
teacher.appendChild(sex);
}
TransformerFactory tff = TransformerFactory.newInstance();
Transformer tf = tff.newTransformer();
tf.transform(new DOMSource(dom),new StreamResult(new File(path + "/my2.xml")));
tf.setOutputProperty(OutputKeys.INDENT,"yes");
}
}
3. sax方式
使用SAX解析器时,需要一个处理器来为各种解析器事件定义事件动作
- startElement() 和 endElement() 在每当遇到起始或终止标签时调用
- characters() 在每当解析到标签体的时候调用
- startDocument() 和 endDocument() 分别在文档开始和结束时各调用一次
public class SaxTest {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
// 读取xml文件
String path = DomTest.class.getResource("").getPath();
File file = new File(path + "/my.xml");
//1.创建Sax解析器工厂
SAXParserFactory factory =SAXParserFactory.newInstance();
//2.通过工厂生产一个sax解析器对象
SAXParser saxPaser = factory.newSAXParser();
//3.创建事件处理器对象
MyHander handler = new MyHander();
//4.开始解析
saxPaser.parse(file, handler);
System.out.println(handler.getStudents());
}
}
/**
* @author liqiye
* @description 自定义的 sax 解析处理器
* @date 2022/5/18
*/
public class MyHander extends DefaultHandler2 {
private List<Student> students;
// 保存临时的数据
private Student student;
// 存储当前解析到的标签,用于characters()
private String current_tag;
// 存储是否是开始标签,用于characters()
private boolean is_start;
//开始解析文档
@Override
public void startDocument() {
System.out.println("开始解析Document");
students = new ArrayList<>();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes){
if("student".equals(qName)){
student = new Student();
student.setId(attributes.getValue("id"));
}
current_tag = qName;
is_start = true;
}
@Override
public void characters(char[] ch, int start, int length){
String contents = new String(ch, start, length).trim();
if("name".equals(current_tag) && is_start){
student.setName(contents);
}
if("age".equals(current_tag) && is_start){
student.setAge(Integer.parseInt(contents));
}
}
@Override
public void endElement(String uri, String localName, String qName){
if("student".equals(qName)){
students.add(student);
}
is_start = false;
}
//解析文档结束
@Override
public void endDocument(){
System.out.println("结束解析Document");
}
public List<Student> getStudents() {
return students;
}
}
@Data
public class Student {
private String id;
private String name;
private Integer age;
}
总结
欢迎指出我的错误!