sax和dom两种xml解析器的区别


前言

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;

}

总结

欢迎指出我的错误!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值