Android开发:XML简介及DOM、SAX、PULL解析对比

1. 目录

目录

2. 定义

XML(extensible Markup Language) ,是一种数据标记语言 & 传输格式

3. 作用

  • 对数据进行标记(结构化数据)
  • 对数据进行存储
  • 对数据进行传输

与html的区别:html用于显示信息;xml用于存储&传输信息

4. XML特点

  • 标签可进行自定义
    XML允许作者定义自己的标签和文档结构
  • 自我描述性

    XML文档实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!-- XML版本(1.0)和所使用编码方法-->
    <note>
    <!-- 根元素 -->
    <to>George</to>
    <from>John</from>
    <heading>Reminder</heading>
    <body>Dont't forget the meeting!</body>
    <!-- 根元素下的4个子元素-->
    </note>
    <!-- 根元素的结尾 -->

仅仅是一个纯文本,有文本处理能力的软件都可以处理xml

  • 可拓展性
    在不中断解析、应用程序的情况下进行拓展。
  • 可跨平台数据传输
    可在不兼容的系统之间进行交换数据,降低了复杂性
  • 数据共享方便
    XML以纯文本进行存储,独立于软件、硬件和应用程序的数据存储方式,使得不同应用程序、软件和硬件都能访问xml的数据

5. 语法

  • 元素要关闭标签
    < p >this is a bitch <p>
  • 对大小写敏感

    1
    2
    < P >这是错误的<p>
    < p >这是正确的 <p>
  • 必须要有根元素(父元素)

    1
    2
    3
    4
    <root>
    <kid>
    </kid>
    </root>
  • 属性值必须加引号

    1
    2
    <note date="16/08/08">
    </note>
  • 实体引用

实体引用 符号 含义
&lt; < 小于
&gt ; > 大于
&amp; & 和浩
&apos; 单引号
&quot; 双引号

元素不能使用&(实体的开始)和<(新元素的开始)

  • 注释
    <!-- This is a comment -->
  • XML的元素、属性和属性值

    文档实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <bookstore>
    <book category="CHILDREN">
    <title lang="en"> Harry Potter </title>
    <author> JK.Rowling</author>
    </book>
    <book category="WEB">
    <title lang="en"> woshiPM </title>
    <author>Carson_Ho</author>
    </book>
    </bookstore>

其中,是根元素;是子元素,也是元素类型之一;而中含有属性,即category,属性值是CHILDREN;而元素则拥有文本内容( JK.Rowling)

  • 元素与属性的差别
    属性即提供元素额外的信息,但不属于数据组成部分的信息。

    范例一

    1
    2
    3
    4
    5
    <bookstore>
    <book category="CHILDREN">
    <title lang="en"> Harry Potter </title>
    <author> JK.Rowling</author>
    </book>

范例二

1
2
3
4
5
6
<bookstore>
<book >
<category>CHILDREN<category>
<title lang="en"> Harry Potter </title>
<author> JK.Rowling</author>
</book>

范例一和二提供的信息是完全相同的。

一般情况下,请使用元素,因为

  1. 属性无法描述树结构(元素可以)
  2. 属性不容易拓展(元素可以)

使用属性的情况:用于分配ID索引,用于标识XML元素。

实例

1
2
3
4
5
6
7
8
9
10
11
12
<bookstore>
<book id = "501">
<category>CHILDREN<category>
<title lang="en"> Harry Potter </title>
<author> JK.Rowling</author>
</book>
<book id = "502">
<category>CHILDREN<category>
<title lang="en"> Harry Potter </title>
<author> JK.Rowling</author>
</book>
<bookstore>

上述属性(id)仅用于标识不同的便签,并不是数据的组成部分

  • XML元素命名规则
    1. 不能以数字或标点符号开头
    2. 不能包含空格
    3. 不能以xml开头
  • CDATA
    不被解析器解析的文本数据,所有xml文档都会被解析器解析(cdata区段除外)
    <![CDATA["传输的文本 "]]>

  • PCDATA
    被解析的字符数据

6. XML树结构

XML文档中的元素会形成一种树结构,从根部开始,然后拓展到每个树叶(节点),下面将以实例说明XML的树结构。

  • 假设一个XML文件如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version ="1.0" encoding="UTF-8"?>
    <简历>
    <基本资料>
    <求职意向>
    <自我评价>
    <其他信息>
    <联系方式>
    <我的作品>
    </简历>
  • 其树结构如下
    树结构 .png

  • XML节点解释
    XML文件是由节点构成的。它的第一个节点为“根节点”。一个XML文件必须有且只能有一个根节点,其他节点都必须是它的子节点。

    this 代表整个XML文件,它的根节点就是 this.firstChild 。 this.firstChild.childNodes 则返回由根节点的所有子节点组成的节点数组。

  每个子节点又可以有自己的子节点。节点编号由0开始,根节点的第一个子节点为 this.firstChild.childNodes[0],它的子节点数组就是this.firstChild.childNodes[0].childNodes 。


根节点第一个子节点的第二个子节点 this.firstChild.childNodes[0].childNodes[1],它返回的是一个XML对象(Object) 。这里需要特别注意,节点标签之间的数据本身也视为一个节点 this.firstChild.childNodes[0].childNodes[1].firstChild ,而不是一个值。


我们解析XML的最终目的当然就是获得数据的值:this.firstChild.childNodes[0].childNodes[1].firstChild.nodeValue 。

请注意区分:节点名称(<性别></性别>)和之间的文本内容(男)可以当作是节点,也可以当作是一个值

节点:
名称:this.firstChild.childNodes[0].childNodes[1]
文本内容:this.firstChild.childNodes[0].childNodes[1].firstChild

值:
名称:this.firstChild.childNodes[0].childNodes[1].nodeValue
(节点名称有时也是我们需要的数据)
文本内容:this.firstChild.childNodes[0].childNodes[1].nodeName

在了解完XML之后,是时候来学下如何进行XML的解析了

7. XML解析

解析XML,即从XML中提取有用的信息

解析方式

基于文档驱动方式

  • 主流方式:DOM方式
  • 简介:XML DOM(XML Document Object Model),XML文件对象模型,定义了访问操作xml文档元素的方法和接口
  • 工作原理: DOM是基于树形结构的的节点的文档驱动方法。使用DOM对XML文件进行操作时,首先解析器读入整个XML文档到内存中,然后解析全部文件,并将文件分为独立的元素、属性等,以树结构的形式在内存中对XML文件进行表示,开发人员通过使用DOM API遍历XML树,根据需要修改文档或检索所需数据
DOM解析
  • 假设需要解析的XML文档如下(subject.xml)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?xml version ="1.0" encoding="UTF-8"?>`
    <code>
    <language id="1">
    <name>Java</name>
    <usage>Android</usage>
    </language>
    <language id="2">
    <name>Swift#</name>
    <usage>iOS</usage>
    </language>
    <language id="3">
    <name>Html5</name>
    <usage>Web</usage>
    </language>
    </code>
  • 核心代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public static List<subject> getSubjectList(InputStream stream)
    { tv = (TextView)findViewById(R.id.tv);
    try {
    //打开xml文件到输入流
    InputStream stream = getAssets().open("subject.xml");
    //得到 DocumentBuilderFactory 对象
    DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
    //得到DocumentBuilder对象
    DocumentBuilder builder = builderFactory.newDocumentBuilder();
    //建立Document存放整个xml的Document对象数据
    Document document = builder.parse(stream);
    //得到 XML数据的"根节点"
    Element element = document.getDocumentElement();
    //获取根节点的所有language的节点
    NodeList list = element.getElementsByTagName("language");
    //遍历所有节点
    for (int i= 0;i<=list.getLength();i++){
    //获取lan的所有子元素
    Element language = (Element) list.item(i);
    //获取language的属性(这里即为id)并显示
    tv.append(lan.getAttribute("id")+"\n");
    //获取language的子元素 name 并显示 tv.append(sub.getElementsByTagName("name").item(0).getTextContent()+"\n");
    //获取language的子元素usage 并显示 tv.append(sub.getElementsByTagName("usage").item(0).getTextContent()+"\n");
    }

总结Dom解析的步骤

1、调用 DocumentBuilderFactory.newInstance() 方法得到 DOM 解析器工厂类实例。
2、调用解析器工厂实例类的 newDocumentBuilder() 方法得到 DOM 解析器对象
3、调用 DOM 解析器对象的 parse() 方法解析 XML 文档得到代表整个文档的 Document 对象。

基于事件驱动

  • 主流方式:SAX、PULL方式
  • 解析方式:可直接根据需要读取所需的JSON数据,不需要像DOM方法把文档先入到内存中
PULL解析
  • 工作原理:PULL的解析方式与SAX解析类似,都是基于事件的模式。
    PULL提供了开始元素和结束元素。当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据,与SAX不同的是,在PULL解析过程中触发相应的事件调用方法返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法从而执行代码。当解释到一个文档结束时,自动生成EndDocument事件。
  • 核心代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    public class MainActivity extends Activity {
    private EditText et;
    private Button myButton;
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    myButton = (Button) this.findViewById(R.id.btn01);
    et = (EditText) this.findViewById(R.id.edittext01);
    myButton.setOnClickListener(new OnClickListener() {
    //可变字符序列,比StringBuffer块
    StringBuilder sb = new StringBuilder("");
    Resources res = getResources();
    XmlResourceParser xrp = res.getXml(R.xml.subject);
    @Override
    public void onClick(View v) {
    int counter = 0;
    try {
    // 判断是否到了文件的结尾
    while (xrp.getEventType() != XmlPullParser.END_DOCUMENT) {
    //文件的内容的起始标签开始,这里的起始标签是subject.xml文件里面<subjects>标签下面的第一个标签
    int eventType=xrp.getEventType();
    switch (eventType) {
    case XmlPullParser.START_DOCUMENT:
    break;
    case XmlPullParser.START_TAG:
    String tagname = xrp.getName();
    if (tagname.endsWith("language")) {
    counter++;
    sb.append("这是第" + counter + "种语言"+"\n");
    //可以调用XmlPullParser的getAttributte()方法来获取属性的值
    sb.append("语言id是:"+xrp.getAttributeValue(0)+"\n");
    }
    else if(tagname.equals("name")){
    //可以调用XmlPullParser的nextText()方法来获取节点的值
    sb.append("语言名称是:"+xrp.nextText()+"\n");
    }
    else if(tagname.equals("teacher")){
    sb.append("用途是:"+xrp.nextText()+"\n");
    }
    break;
    case XmlPullParser.END_TAG:
    break;
    case XmlPullParser.TEXT:
    break;
    }
    //解析下一个事件
    xrp.next();
    }
    //StringBuilder要调用toString()方法并显示
    et.setText(sb.toString());
    } catch (XmlPullParserException e) {
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    });
    }

SAX解析

  • 工作原理:基于事件驱动,在读取XML文档内容时,事件源顺序地对文档进行扫描,当扫描到文档的开始与结束(Document)标签、节点元素的开始与结束(Element)标签时,直接调用对应的方法,并将状态信息以参数的形式传递到方法中,然后我们可以依据状态信息来执行相关的自定义操作。

同样是采用事件驱动进行解析,但相比pull解析方法,采用SAX方式进行XML解析可能会较为复杂,这里就不作实例展示,有兴趣的童鞋们可以自己去尝试下,毕竟实践出真知

DOM、SAX、PULL三类方式对比

DOM方式

  • 原理:基于文档驱动,是先把dom全部文件读入到内存中,构建一个主流内存的树结构,然后使用DOM的API遍历所有数据,调用API检索想要的数据和操作数据。
    所以,DOM方式的优缺点是:
  • 特点:
    优点:整个文档树存在内存中,可对XML文档进行操作:删除、修改等等;可多次访问已解析的文档;由于在内存中以树形结构存放,因此检索和更新效率会更高。;
    缺点:解析 XML 文件时会将整个 XML 文件的内容解析成树型结构存放在内存中并创建新对象,比较消耗时间和内存;
  • 使用情境
    对于像手机这样的移动设备来讲,内存是非常有限的,在XML文档比较小、需要对解析文档进行一定的操作且一旦解析了文档需要多次访问这些数据的情况下可以考虑使用DOM方式,因为其检索和解析效率较高

SAX方式

  • 原理:基于事件驱动,在读取XML文档内容时,事件源顺序地对文档进行扫描,当扫描到文档的开始与结束(Document)标签、节点元素的开始与结束(Element)标签时,直接调用对应的方法,并将状态信息以参数的形式传递到方法中,然后我们可以依据状态信息来执行相关的自定义操作。
  • 特点:
    优点:解析效率高、占存少、灵活性高
    缺点:解析方法复杂(API接口复杂),代码量大;可拓展性差:无法对 XML 树内容结构进行任何修改
  • 使用情境
    适用于需要处理大型 XML 文档、性能要求较高、不需要对解析文档进行修改且不需要对解析文档多次访问的场合

PULL方式

  • 原理:PULL的解析方式与SAX解析类似,都是基于事件的模式。
    PULL提供了开始元素和结束元素。当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据,与SAX不同的是,在PULL解析过程中触发相应的事件调用方法返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法从而执行代码。当解释到一个文档结束时,自动生成EndDocument事件。
  • 特点:
    优点:SAX的优点PULL都有,而且解析方法比SAX更加简单
    缺点:可拓展性差:无法对 XML 树内容结构进行任何修改

  • 使用情境
    适用于需要处理大型 XML 文档、性能要求较高、不需要对解析文档进行修改且不需要对解析文档多次访问的场合

同样的使用情景,在SAX和PULL解析方法中,更加推荐PULL方法

总结

本文对现今主流的数据传输格式XML进行了简单的介绍,希望大家实践出真知哦!


欢迎关注Carson_Ho的简书!

不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度

文章目录
  1. 1. 1. 目录
  2. 2. 2. 定义
  3. 3. 3. 作用
  4. 4. 4. XML特点
  5. 5. 5. 语法
  6. 6. 6. XML树结构
  7. 7. 7. XML解析
    1. 7.0.0.0.1. 解析方式
    2. 7.0.0.0.2. DOM解析
    3. 7.0.0.0.3. PULL解析
  • 7.0.1. DOM、SAX、PULL三类方式对比
  • 7.0.2. 总结
  • 7.0.3. 欢迎关注Carson_Ho的简书!
  • ,