java实现XML解析(SAX、PULL)

一、XML基础
1.1 什么是XML

  • XML是指可扩展标记语言(eXtensible Markup Language),它也是一种标记语言,很类似HTML。它被设计的宗旨是存储数据,而非显示数据。
  • Html展示数据
  • XML标签没有被预定义,需要用户自行定义标签。
  • XML技术是W3C组织(World Wide Web Consortium万维网联盟)发布的,目前遵循的是W3C组织于2000年发布的XML1.0规范。
  • XML被广泛认为是继Java之后在Internet上最激动人心的新技术。
  • W3CSchool(入门级)

1.2 XML常见应用

  • 用于数据存储
  • 用在软件配置,用于描述模块之间的关系(如著名的Struts、Spring和Hibernate都是基于XML作为配置文件的–SSH)android都是xml。 图形界面都是用的xml
  • 在一个软件系统中,通过XML配置文件可以提高系统的灵活性。即程序的行为是通过XML文件来配置的,而不是硬编码。
  • XML是一种通用的数据交换格式。(跨平台)

二、XML语法
语法较多,建议先直接上手写案例,成功之后再回来当回顾学习用
一个XML文件主要包括以下几个部分:

  • 文档声明
    • 在编写XML文档时,需要先使用文档声明来声明XML文档。且必须出现在文档的第一行。(前面注释都不允许)
      • 最简单的语法:
    • 用encoding属性说明文档所使用的字符编码。保存在磁盘上的文件编码要与声明的编码一致。
      • 如:
    • 用standalone属性说明文档是否独立,即是否依赖其他文档。
      • 如:
  • 元素
    • XML元素指XML文件中出现的标签。一个标签分为起始和结束标签(不能省略)。一个标签有如下几种书写形式:
      • 包含标签主体:some content
      • 不含标签主体:(有时也叫空标签)
      • 一个标签中可以嵌套若干子标签,但所有标签必须合理的嵌套,不允许有交叉嵌套。
    • 一个XML文档必须有且仅有一个根标签,其他标签都是这个根标签的子标签或孙标签。
    • 对于XML标签中出现的所有空格和换行,XML解析程序都会当作标签内容进行处理。例如:下面两段内容的意义是不一样的。
      • 第一段:
        • <网址>
        • ww.1000phone.copm
      • 第二段:
      • <网址>ww.1000phone.copm
    • 由于在XML中,空格和换行都作为原始内容被处理,所以,在解析XML文件时要特殊处理下,绕过这些空格和换行符。
    • 元素命名规范:一个XML元素可以包含字母、数字以及其它一些可见字符,但必须遵守下面的一些规范
      • 区分大小写,例如,和是两个不同的标记。(与java、c变量命名一致)
      • 不能以数字或"_" (下划线)开头。
      • 不能以xml(或XML、或Xml 等)开头。(xml 为关键字,所以不能重复)
      • 不能包含空格。
      • 名称中间不能包含冒号(:)。
      • 一句话:像个正常人一样去写名称就行,不用关键字
  • 属性
    • 一个元素可以有多个属性,每个属性都有它自己的名称和取值,例如:
    • 属性值一定要用引号(单引号或双引号)引起来。
    • 属性名称的命名规范与元素的命名规范相同
    • 元素中属性没有顺序要求,但是不准重复。
    • 在XML技术中,标签属性所代表的信息也可以被改成用子元素的形式来描述
  • 注释
    • XML中的注释语法为:
    • 注意:
      • XML声明之前不能有注释(xml声明必须放在文档第一行)
      • 注释不能嵌套
  • 实体引用及CDATA区
    • 实体引用
      • 在 XML 中,一些字符拥有特殊的意义。如果你把字符 “<” 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始。如下这样会产生 XML 错误:if salary < 1000 then
      • 为了避免此类错误,需要把字符 “<” 替换为实体引用,就像这样: < salary < 1000 then
      • 在 XML 中有 5 个预定义的实体引用
        • < < 小于
        • > > 大于
        • & & 和号
        • ' …… 省略号
        • " “” 引号
    • CDATA区
      • 术语 CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)。
      • 在 XML 元素中,“<” 和 “&” 是非法的。
      • “<” 会产生错误,因为解析器会把该字符解释为新元素的开始。
      • “&” 也会产生错误,因为解析器会把该字符解释为字符实体的开始。
      • 某些文本,比如 JavaScript代码,包含大量 “<” 或 “&” 字符。为了避免错误,可以将脚本代码定义为CDATA。
      • CDATA 部分中的所有内容都会被解析器忽略。
      • CDATA 部分由 “” 结束
  • 处理指令(PI:Processing Instruction)
    • 处理指令,简称PI(Processing Instruction)。
    • 作用:用来指挥软件如何解析XML文档。
    • 语法:必须以“”作为结尾。
    • 常用处理指令:
      • XML声明:
      • xml-stylesheet指令:作用:指示XML文档所使用的CSS样式XSLT。
  • Xml约束
    • 为什么需要约束:
      • XML都是用户自定义的标签,若出现小小的错误,软件程序将不能正确地获取文件中的内容而报错。
    • 什么是XML约束:
      • 约束文档定义了在XML中允许出现的元素名称、属性及元素出现的顺序等等。
      • 在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。
      • 常用的约束技术XML DTD, XML Schema
      • 作为普通工程师,一般是使用别人的约束文件,而不会自己去写约束。例如使用SSH框架开始时用到的xml文件,均要受框架的提供的xml的约束文件的约束

三、SAX解析
XML解析方式主要有:
DOM(文档对象模型):将整个文件以树的结构存储到内存中,适合小文件,程序可读性高。

SAX(基于事件流的解析):省内存,程序可读性相比上述较差。
PULL(第三方jar包):也是基于事件流的解析
本次,我们只学习SAX和PULL两种方式
正式来学习SAX解析
Simple APIs for XML,XML简单应用程序接口,在javax.xml.parsers包中,SAX解析是JDK自带的解析方式,包括一组接口和类

3.1 sax解析原理:以事件驱动的方式解析,即找开始结束标签的方式
SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
所以有两个关键点:顺序扫描,事件处理函数
3.2 案例:解析指定文件(标签无属性),并将文件中的信息存放到list中。

  • 3.2.1、需要先创建XML文件(students.xml)



        

        
        
        
        
        
]>

    
        张三
        19
        man
    
    
        李四
        20
        man
    
    
        王五
        25
        woman
    

3.2.2 解析指定文件(根标签有属性),并将文件中的信息存放到list中

package cmo.qfedu;


import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

/**
* sax 解析
* @ClassName SaxTest
* @Author ykx
* @Date 2022/8/31 22:10
* @Version 1.0
*/
public class SaxTest {
    public static void main(String[] args) throws Exception {
        //1.创建解析器工厂
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        //2.通过解析器工厂对象调用方法
        SAXParser saxParser = saxParserFactory.newSAXParser();
        //3.创建处理器
        MyHandler myHandler = new MyHandler();
        //4.用解析器对象调用解析方法
        saxParser.parse("src/students.xml",myHandler);

        for (Student student : myHandler.students) {
            System.out.println(student);
        }

    }
}

MyHandler.java

package cmo.qfedu;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.List;

/**
* @ClassName MyHandler
* @Author ykx
* @Date 2022/8/31 22:38
* @Version 1.0
*/
public class MyHandler extends DefaultHandler {

    ArrayList students;
    Student student;

    /**
     * 当前正在解析的标签名
     */
    String tag;
    /**
     * 重写父类中开始解析文档的方法
     * @throws SAXException
     */
    @Override
    public void startDocument() throws SAXException {
        students = new ArrayList<>();
    }

    /**
     * 重写父类中开始解析标签的方法
     * @throws SAXException
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        tag=qName;
        if ("student".equals(tag)) {
            student = new Student();
            int count = attributes.getLength();
            for (int i = 0; i < count; i++) {
                if ("sid".equals(attributes.getQName(i))) {
                    student.setSid(attributes.getValue(i));
                }
            }
        }
    }

    /**
     * 重写父类中解析标签内容的方法
     * @param ch
     * @param start
     * @param length
     * @throws SAXException
     */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if ("sname".equals(tag)){
            student.setSname(new String(ch,start,length));
        }else if ("sage".equals(tag)){
            student.setSage(Integer.parseInt(new String(ch,start,length)));
        }else if ("ssex".equals(tag)){
            student.setSsex(new String(ch,start,length));
        }
    }

    /**
     * 重写父类中结束解析标签的方法
     * @throws SAXException
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if ("student".equals(qName)){
            students.add(student);
        }
    }

    /**
     * 重写父类中结束解析文档的方法
     * @throws SAXException
     */
    @Override
    public void endDocument() throws SAXException {
        System.out.println("文档解析完成");
    }




}

运行结果:

文档解析完成
Student{sid='100', sname='张三', sage=19, ssex='man'}
Student{sid='101', sname='李四', sage=20, ssex='man'}
Student{sid='102', sname='王五', sage=25, ssex='woman'}


Process finished with exit code 0

四、pull解析
pull解析器的方式,和sax解析一样,也是采用事件驱动进行解析的,在eclipse程序中没有内置的支持pull解析的jar包,所以我们需要添加第三方的jar包来支持pull。

案例:解析XML文件(不带属性)
需要准备jar包:kxml2-2.2.2.jar

package cmo.qfedu;


import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;


import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;


/**
* @ClassName PullTest
* @Author ykx
* @Date 2022/9/1 0:32
* @Version 1.0
*/
public class PullTest {
    public static void main(String[] args) throws Exception {
        //创建pull解析器工厂对象
        XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
        //创建pull解析器
        XmlPullParser xmlPullParser = xmlPullParserFactory.newPullParser();
        //指定数据源,解析students.xml
        xmlPullParser.setInput(new FileReader("src/students.xml"));
        //获取其状态码,区分现在是哪个事件源(文档开头,文档结尾,标签开头,标签结尾)
        int eventType = xmlPullParser.getEventType();


        List studentList = new ArrayList<>();
        Student student = null;
        //循环解析,直至文件解析结束
        while (eventType!=XmlPullParser.END_DOCUMENT){
            //获取标签名称
            String name = xmlPullParser.getName();
            if (eventType==XmlPullParser.START_DOCUMENT){
                System.out.println("开始解析文档");
            }else if (eventType==XmlPullParser.START_TAG){
                switch (name){
                    case "student":
                        student = new Student();
                        int count = xmlPullParser.getAttributeCount();
                        for (int i = 0; i < count; i++) {
                            String attributeName = xmlPullParser.getAttributeName(i);
                            String attributeValue = xmlPullParser.getAttributeValue(i);
                            if ("sid".equals(attributeName)){
                                student.setSid(attributeValue);
                            }
                        }
                        break;
                    case "sname":
                        //**获取标签的文本内容**
                        student.setSname(xmlPullParser.nextText());
                        break;
                    case "sage":
                        student.setSage(Integer.parseInt(xmlPullParser.nextText()));
                        break;
                    case "ssex":
                        student.setSsex(xmlPullParser.nextText());
                        break;
                }
            }else if (eventType==XmlPullParser.END_TAG){
                if ("student".equals(name)){
                    studentList.add(student);
                }


            }
            //让指针继续往下解析下一个事件源
            eventType = xmlPullParser.next();


        }


        for (Student student1 : studentList) {
            System.out.println(student1);
        }


    }
}

PULL vs SAX
Pull解析器和SAX解析器虽有相似性但也有区别,他们的区别为:

  • SAX解析器的工作方式是自动将事件推入注册的事件处理器进行处理,因此你不能控制事件的处理主动结束;
  • 而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。

你可能感兴趣的:(java,xml)