zoukankan      html  css  js  c++  java
  • 二、Android XML数据解析

    XML,可扩展标记语言。可以用来存储数据,可以看做是一个小型的数据库,SharedPreference就是使用XML文件存储数据的,SQLite底层也是一个XML文件,而在网络应用方面,通常作为信息的载体,通常把数据包装成XML来传递。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
          -----文档开始
    <persons> -----开始元素(persons)
    <person id = "11"> -----文本节点(空白文本) 开始元素(person)属性
    <name>Coder-pig</name> -----文本节点(空白文本) 开始元素(name)属性 结束元素
    <age>18</age> -----文本节点(空白文本) 开始元素(age)属性 结束元素
    </person> -----文本节点(空白文本) 结束元素
    <person id = "13">
    <name>Jay</name>
    <age>20</age>
    </person>
    </persons> -----结束元素(persons)
    -----文档结束

    上面就简单的定义了一个存储person对象的xml文件的编码,注意,外面的空白区域也是文本节点。

    2. 三种解析XML方法的比较

    2.1 SAX解析XML

    对文档进行顺序扫描,当扫描到文档(doucument)开始与结束、元素(element)开始与结束等地方时,通知事件处理函数,由事件处理函数做相对应动作,然后继续进行同样的扫描,直至文档结束。解释速度快,占用内存小,,每需要解析一类XML,就需要编写新的适合该类的XML处理类,比较麻烦。采用的是流式解析,解析是同步的,读到哪就处理到哪。

    2.2 Dom解析XML

    先把XML文档读取到内存中,然后再用DOM API来访问树形结构,并获取数据。这个写起来很简单,但是很消耗内存,假如读取的数据量大,手机内存不够的话,可能导致手机死机。不建议在Android设备中使用,解析简单的XML可以。常用的五个接口与类:DocculemElementNodeNodeListDOMParser,Don是整个文件解析到内存中,供用户需要的节点信息,支持随机访问。

    2.3 pull解析XML

    XML pull提供了开始元素和结束元素。当某个元素开始时,可以调用parsernextText从XML文档中提取所有字符数据。当解析到文档结束时,自动生成EndDocument。常用接口和类:XmlPullParserXmlSerializerXmlPullParserFactory。和SAX差不多,代码实现比较简单,非常适合移动设备,Android系统内置pull解析器,而且Android系统内部默认使用pull来解析XML文件。

    3. SAX解析XML

    SAX是一个解析速度快且占用内存少的XML解析器,非常适合用于Android等移动设备;SAX解析XML文件采用的是事件驱动,也就是说不需要解析整个文档,而是在解析过程中,判断读取的字符是否符合XML语法的某部分(文档开头,文档结束,或者标签开头和标签结束),符合的话就会触发事件(回调方法),而这些方法都定义在ContentHandler接口中,而ContentHandler是一个接口, 使用起来不方便,所以Android准备了一个帮助类DefaultHandler,只需要继承这个类,重写里面对应的方法即可。

    可以重写的方法:

    • startDocument():当读取到文文档开始标志时触发,通常在这里完成一些初始化操作。
    • endDocument():文档结束部分,在这里完成一些善后工作。
    • startElement(names, paceURI, localName, qName, atts):参数依次问命名空间,不带命名空间的前缀标签名,带命名空间的前缀标签名,通过atts可以得到所有的属性名和相应的值;SAX中一个重要的特点就是它的流式处理,当遇到一个标签时,它并不会记录下以前遇到的标签,就是说,在startElement()中,所有知道的信息就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元素等其他与结构相关的信息,都不知道,需要程序来完成,这使得SAX在编程处理上没有DOM方便。
    • endElement(uri, localName, name):在遇到结束标签的时候,调用该方法。
    • characters(ch, start, length):这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch, start, length)就可以获取内容。

    核心代码:SAX解析类——SaxHelper.java

    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
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    package com.ly.allendemowebservice;

    import android.util.Log;

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

    import java.util.ArrayList;


    * SAX解析XML
    *
    * @author Liuyang
    * @date 2019/7/26
    */
    public class extends DefaultHandler {
    private static final String TAG = "SaxHelper";

    private Person mPerson;
    private ArrayList<Person> mPersons;

    * 当前解析的元素标签
    */
    private String mTagName = null;


    * 当读取到文档开始标志时触发,通常在里面完成一些初始化操作
    */
    @Override
    public void startDocument() throws SAXException {
    this.mPersons = new ArrayList<>();
    Log.i(TAG, "读取到文档头,开始解析xml");
    }

    private static final String ELEMENT = "person";


    * 读到一个开始标签时触发,第二个参数为标签名,最后一个参数为属性数组
    */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    if (ELEMENT.equals(localName)) {
    mPerson = new Person();
    mPerson.setId(Integer.parseInt(attributes.getValue("id")));
    Log.i(TAG, "开始处理person元素~");
    }
    mTagName = localName;
    }

    private static final String TAG_NAME1 = "name";
    private static final String TAG_NAME2 = "age";


    * 读取标签里面的内容,第一个参数为字符串内容,后面依次为起始位置和长度
    */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
    // 判断当前标签是否有效
    if (mTagName != null) {
    String data = new String(ch, start, length);
    // 读取标签内容
    if (TAG_NAME1.equals(mTagName)) {
    mPerson.setName(data);
    Log.i(TAG, "处理name元素内容");
    } else if (TAG_NAME2.equals(mTagName)) {
    mPerson.setAge(Integer.parseInt(data));
    Log.i(TAG, "处理age元素内容");
    }
    }
    }


    * 读取到元素结束时触发,这里将对象添加到集合中
    */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
    if (ELEMENT.equals(localName)) {
    mPersons.add(mPerson);
    mPerson = null;
    Log.i(TAG, "处理person元素结束~");
    }
    mTagName = null;
    }


    * 读取到文档结束时触发,
    */
    @Override
    public void endDocument() throws SAXException {
    super.endDocument();
    Log.i(TAG, "处理person元素结束~");
    }


    * 获取persons集合
    */
    public ArrayList<Person> getPersons() {
    return mPersons;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    private ArrayList<Person> readXmlForSax() throws IOException, ParserConfigurationException, SAXException {
    // 获取文件资源建立输入流对象,可能是从网络获取
    InputStream inputStream = getAssets().open("person1.xml");
    // 创建XML解析器
    SaxHelper helper = new SaxHelper();
    // 得到SAX解析工厂
    SAXParserFactory factory = SAXParserFactory.newInstance();
    // 创建SAX解析器
    SAXParser parser = factory.newSAXParser();
    // 将XML解析器分配给解析器,对文档进行解析,将事件发送给处理器
    parser.parse(inputStream, helper);
    inputStream.close();
    return helper.getPersons();
    }

    在项目assets目录下有一个文件person1.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    <persons>
    <person id = "11">
    <name>SAX解析</name>
    <age>18</age>
    </person>
    <person id = "13">
    <name>XML1</name>
    <age>43</age>
    </person>
    </persons>

    Demo地址:Allen_Demo_WebService

    4. DOM解析XML数据

    DOM解析XML文件时会将文件所有的内容以文档树的形式存放在内存中,可以使用DOM API遍历XML树,检索到需要的数据。使用DOM操作XML的代码比较直观,并且在编码方面比基于SAX的实现更加简单。但是DOM需要将XML文件的所有内容存放到内存中,所以内存消耗大,特别是对于Android设备而言,内存资源有限,因此建议使用前面的SAX解析。如果解析的内容比较小,也可以使用DOM来解析。

    DOM API:

    • DocumentBuilderFactory(解析器工厂类):创建方法DoucmentBuilderFactory factory = DoucmentBuilderFactory.newInstance();

    • DocumentBuilder(解析器类):创建方法:通过解析器工厂类来获得DocumentBuidler builder = factory.newDocumentBuilder();

    • Document(文档树模型):将需要解析的XML文件读入DOM解析器:Document doc = builder.parse(context.getAssets().open("person2.xml"));

      Document对象代表了一个XML文档的模型,所有的其他Node都以一定的顺序包含在Document对象内,排列成树状,以后对XML文档的所有操作都与解析器无关。

    • NodeList(列表类):代表一个包含一个或多个Node的列表,有以下两个方法:

      • item(index):返回集合的第indexNode项;
      • getLength():列表的节点数
    • Node(节点类):DOM中最基本的对象,代表文档树中的抽象节点,很少会直接使用;通常调用其子对象的ElementAttrText等。

    • Element(元素类):Node最主要的子对象,在元素中可以包含属性,因此有获取属性的方法:

      • getAttrbute():获取属性值
      • getTagName():获取元素名称
    • Attr(属性类):代表某个元素的属性,虽然Attr继承自Node接口,但因为Attr是包含在Element中的,但并不能将其看做是Element的子对象,因为Attr并不是DOM树的一部分。

    核心代码:DOM解析类——DomHelper.java

    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
    61
    62
    63
    大专栏  二、Android XML数据解析">64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    package com.ly.allendemowebservice;

    import android.content.Context;
    import android.util.Log;

    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    import org.xml.sax.SAXException;

    import java.io.IOException;
    import java.util.ArrayList;

    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.ParserConfigurationException;


    * DOM解析XML
    *
    * @author Liuyang
    * @date 2019/7/26
    */
    public class DomHelper {
    private static final String TAG = "DomHelper";
    private static final String TAG_NAME = "name";
    private static final String TAG_AGE = "age";

    public static ArrayList<Person> queryXML(Context context) {
    ArrayList<Person> persons = new ArrayList<>();
    try {
    // 1. 获取DOM解析器工厂
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    // 2. 获取DOM解析器
    DocumentBuilder builder = factory.newDocumentBuilder();
    // 3. 将要解析的XML文件读入DOM解析器
    Document document = builder.parse(context.getAssets().open("person2.xml"));

    Log.i(TAG, "处理该文档的DomImplementation对象 = " + document.getImplementation());

    // 4. 得到文档中名称为person的元素的节点元素
    NodeList nodeList = document.getElementsByTagName("person");
    // 5. 遍历集合,显示集合中的元素以及子元素的名字
    for (int i = 0; i < nodeList.getLength(); i++) {
    // 先从person元素开始解析
    Element personElement = (Element) nodeList.item(i);
    Person person = new Person();
    person.setId(Integer.valueOf(personElement.getAttribute("id")));

    // 获取person下的name和age的Note集合
    NodeList childNodeList = personElement.getChildNodes();
    for (int j = 0; j < childNodeList.getLength(); j++) {
    Node childNode = childNodeList.item(j);
    // 判断子Node类型是否为元素的Node
    if (childNode.getNodeType() == Node.ELEMENT_NODE) {
    Element childElement = (Element) childNode;
    if (TAG_NAME.equals(childElement.getNodeName())) {
    person.setName(childElement.getFirstChild().getNodeValue());
    } else if (TAG_AGE.equals(childElement.getNodeName())) {
    person.setAge(Integer.valueOf(childElement.getFirstChild().getNodeValue()));
    }
    }
    }
    persons.add(person);
    }
    } catch (ParserConfigurationException e) {
    e.printStackTrace();
    } catch (SAXException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    return persons;
    }
    }

    5. PULL解析XML数据

    除了SAX和DOM解析XML之外,Android系统内置了Pull解析器用来解析XML,比如SharedPreference就是使用内置的pull解析配置文件的。它的使用和SAX类似,都是采用事件驱动来完成XML的解析,而pull代码比较简单,只需处理开始和结束的事件,通常使用switch语句,根据事件不同的类型,匹配不同的处理方式,有五种事件:START_DOCUMENTSTART_TAGTEXTEND_TAGEND_DUCOMENT

    XML pull 提供了开始元素和结束元素。当某个元素开始的时候,可以调用paser.nextText从XML文档中提取所有字符数据。当解析到一个文档结束时,自动生成EndDocument事件。在PULL解析过程中返回的是数字,且需要自己获取产生事件然后做出相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码:读取到XML的声明返回START_DOCUMENT;结束返回END_DOCUMENT;开始标签返回START_TAG;结束标签返回END_TAG;文本返回TEXT
    使用PULL解析XML的流程:

    1. 获取一个XmlPullPaser类的引用:

      1
      2
      3
      4
      // 方式1:通过xml解析工厂获得实体类
      XmlPullPaserFactory factory = XmlPullPaserFactory.newInstance(); XmlPullPaser paser = factory.newPullPaser();
      // 方式2:直接获得实体类
      XmlPullPaser paser = Xml.newPullPaser();
    2. paser解析器对象提供xml流与编码格式:

      1
      paser.setInput(xml, "UTF-8");
    3. 获得事件的类型:

      1
      int eventType = paser.getEventType();
    4. switch对不同的事件类型进行不同的处理:

    • START_DUCUMENT:开始读文档时触发,在这里完成初始化操作;
    • START_TAG:开始读标签,通过pasergetName()方法获得标签名信息比较,使用getAttributeValue(index)获取属性值;
    • 对于文字节点TEXT可以使用paser.nextText()获得节点内容;
    • END_TAG:标签结束;
    • paser.next():循环解析下一个元素。

    核心代码:PULL解析XML——PullHelper.java

    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
    61
    62
    63
    64
    package com.ly.allendemowebservice;

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

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.ArrayList;


    * PULL解析XML
    *
    * @author Liuyang
    * @date 2019/7/27
    */
    public class PullHelper {
    public static ArrayList<Person> getPersons(InputStream xml) throws XmlPullParserException, IOException {
    ArrayList<Person> persons = null;
    Person person = null;
    // 创建一个XML解析工厂
    XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    // 获得XML解析类的引用
    XmlPullParser parser = factory.newPullParser();
    parser.setInput(xml, "UTF_8");
    // 获得事件类型
    int eventType = parser.getEventType();
    while (eventType != XmlPullParser.END_DOCUMENT) {
    switch (eventType) {
    case XmlPullParser.START_DOCUMENT:
    persons = new ArrayList<>();
    break;
    case XmlPullParser.START_TAG:
    if ("person".equals(parser.getName())) {
    person = new Person();
    // 取出属性值
    int id = Integer.parseInt(parser.getAttributeValue(0));
    person.setId(id);
    } else if ("name".equals(parser.getName())) {
    // 获取该节点的内容
    String name = parser.nextText();
    assert person != null;
    person.setName(name);
    } else if ("age".equals(parser.getName())) {
    int age = Integer.parseInt(parser.nextText());
    assert person != null;
    person.setAge(age);
    }
    break;
    case XmlPullParser.END_TAG:
    if ("person".equals(parser.getName())) {
    assert persons != null;
    persons.add(person);
    person = null;
    }
    break;
    default:
    break;
    }
    eventType = parser.next();
    }
    return persons;
    }
    }

    使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    try {
    InputStream inputStream = getAssets().open("person3.xml");
    ArrayList<Person> persons = PullHelper.getPersons(inputStream);
    for (int i = 0; i < persons.size(); i++) {
    Log.i(TAG, i + " == " + persons.get(i).toString());
    }
    } catch (IOException e) {
    e.printStackTrace();
    } catch (XmlPullParserException e) {
    e.printStackTrace();
    }

    使用PULL生成XML数据的流程:

    1. 创建XMlSerializer(XML序列化类)的实例:

      1
      XmlSerializer serializer = Xml.newSerializer();
    2. XmlSerializer设置输出流与编码格式:

      1
      serializersetOutput(out, "UTF-8");
    3. XMlSerializer设置XML的编码格式:

      1
      serializer.startDocument("UTF-8", true);
    4. 设置根元素:

      1
      serializer.startTag(null, "person");
    5. 使用foreach循环遍历persons集合中所有的元素,同时依次写入标签与属性:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      for(Person p: persons){
      serializer.startTag(null, "person");
      serializer.attribute(null, "id", p.getId() + "");
      serializer.startTag(null, "name");
      serializer.text(p.getName());
      serializer.endTag(null, "name");
      serializer.startTag(null, "age");
      serializer.text(p.getAge() + "");
      serializer.endTag(null, "age");
      serializer.endTag(null, "person");
      }
    6. 设置跟踪完结元素:

      1
      serializer.endTag(null, "persons");
    7. 结束文档编写:

      1
      serializer.endDocument();
    8. 调用flush(),将内存中的数据写入文件中并关闭输出流

      1
      2
      out.flush();
      out.close();

    核心代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public static void save(List<Person> persons, OutputStream out) throws Exception {
    XmlSerializer serializer = Xml.newSerializer();
    serializer.setOutput(out, "UTF-8");
    serializer.startDocument("UTF-8", true);
    serializer.startTag(null, "persons");
    for (Person p : persons) {
    serializer.startTag(null, "person");
    serializer.attribute(null, "id", p.getId() + "");
    serializer.startTag(null, "name");
    serializer.text(p.getName());
    serializer.endTag(null, "name");
    serializer.startTag(null, "age");
    serializer.text(p.getAge() + "");
    serializer.endTag(null, "age");
    serializer.endTag(null, "person");
    }

    serializer.endTag(null, "persons");
    serializer.endDocument();
    out.flush();
    out.close();
    }

    Demo地址:Allen_Demo_WebService

  • 相关阅读:
    Page.EnableViewStateMac 属性
    ASP.NET2.0权限/角色管理表aspnet_Membership解析(转)
    Request.ServerVariables(HTTP_REFERER)
    对象不能从DBNull 转换为其他类型
    ASP.NET角色管理配置
    TextBox保存的文本在Label中显示
    SET NOCOUNT ON
    浅论ViewState及其与Session的关系(转)
    ASP.NET2.0权限/角色管理表aspnet_Applications解析(转)
    动态编辑控件宽高
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12366129.html
Copyright © 2011-2022 走看看