XML的三种解析方式

XML简介

XML,可扩展标记语言(Extensible Markup Language),标准通用语言的子集。XML可以用来标记数据、定义数据类型。它非常适合互联网传输,提供了统一的方法来描述和交换独立于应用程序和供应商的结构化语言。


XML特点

  • 是一种标记语言,类似于HTML
  • 设计宗旨是传输数据,而非显示数据
  • 没有预定义的标签,需要自定义标签
  • 具有自我描述性
  • 是W3C推荐的标准

XML格式

XML可以用来存储少量的数据,在网络层也可以作为服务端与客户端之间数据传输的一种格式。

<?xml version="1.0" encoding="UTF-8"?>
<root>                                                 
    <child>
        <subChild></subChild>
    </child>
</root>
需要解析的文件menu.xml
<?xml version="1.0" encoding="UTF-8"?>
<Menu>
    
    <list  name="热销榜">
        <food>鱼香肉丝</food>
        <food>酸辣土豆丝</food>
        <food>干煸刀豆</food>
        <food>刀豆土豆</food>
        <food>韭黄炒蛋</food>
        <food>椒盐排条</food>
    </list>

    <list  name="限时特价">
        <food>酸汤肥牛</food>
        <food>青椒肉丝毛豆</food>
        <food>红烧小肉</food>
        <food>干锅牛蛙</food>
        <food>水芹香干</food>
        <food>剁椒秋刀鱼</food>
    </list>
    
    <list  name="经济实惠">
        <food>水煮鱼片</food>
        <food>干煎臭豆腐</food>
        <food>鱼香茄子</food>
        <food>香酥蚕豆堡</food>
        <food>外婆菜窝窝头</food>
        <food>毛血旺</food>
    </list>
    
</Menu>

XML的DOM解析

DOM解析会将整个XML文件中的内容以树的形式加载到内存,然后可以进行遍历树检索到需要的数据。
实现解析的套路:

1.获得解析器工厂类
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
2.获得解析器类
DocumentBuilder builder = factory.newDocumentBuilder();
3.将需要解析的XML文件传入解析器
Document doc = builder.parse("data.xml");
4.获得文档根元素
Element root = doc.getDocumentElement();
5.获取根节点下的元素集合,取出数据并保存
NodeList list = root.getChildNodes();

Document的一些API:

  • NodeList:节点列表类,包含一个或多个Node的列表。
  • Node:抽象节点类,通常调用其子节点Element,Attr,Text。
  • Element:元素类,getTagName获取节点名
  • Attr:属性类,代表某个元素的属性

XML的Sax解析

Sax解析是对xml文件进行逐行解析,相对于DOM解析方式效率要高。
Sax解析的常用套路:
1.获得解析器工厂

SAXParserFactory factory = SAXParserFactory.newInstance();

2.通过解析器工厂获得Sax解析器对象

SAXParser parser = factory.newSAXParser();

3.创建处理器MyHandler继承自DefaultHandler,实现其内部的处理方法

class MyHandler extends DefaultHandler{

    @Override
    public void startDocument() throws SAXException {
        //do something
    }

    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        //do something
    }

    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        //do something
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        //do something
    }
}

4.对文档输入流(InputStream)进行解析

parser.parse(is, handler);

使用Sax方式对menu.xml文件进行Pull解析
关键java代码:

private List<String> menu;

private HashMap<String, List<String>> map;

SAXParserFactory factory = SAXParserFactory.newInstance();
        
try {
    SAXParser parser = factory.newSAXParser();
    
    InputStream is = getAssets().open("menus.xml");
    
    MyHandler handler = new MyHandler();
    
    parser.parse(is, handler);
} catch (Exception e) {
    e.printStackTrace();
}

MyHandler.java

class MyHandler extends DefaultHandler{
        
    String tagName = null; 

    @Override
    public void startDocument() throws SAXException {
        map = new HashMap<String, List<String>>();
    }

    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        tagName = qName;
        if("list".equals(qName)){
            menu = new ArrayList<String>();
            name = attributes.getValue("name");
        }
        
    }

    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        tagName = "";
        if("list".equals(qName)){
            map.put(name, menu);
        }
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        String value = new String(ch,start,length);
        if("food".equals(tagName)){
            menu.add(value);
        }
    }
}

XML的Pull解析

Pull解析的常用套路:
1.通过解析器工厂获得pull解析器对象

XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();

2.设置需解析的输入流(InputStream)和编码方式

parser.setInput(is,"utf-8");

3.获取事件类型

int eventType = parser.getEventType();

4.根据事件类型和标签名做出相应处理,如读取和保存数据

while(eventType != XmlPullParser.END_DOCUMENT) {
    // 获得当前节点的名称
    String tagName = parser.getName();      
    switch (eventType) {
        // 当前等于开始节点
        case XmlPullParser.START_TAG:         
           //相关操作         
           break;
        // 当前等于结束节点
        case XmlPullParser.END_TAG:                
           //相关操作          
           break;

        default:
           break;
    }
    // 下一个
    eventType = parser.next();
}

使用Pull方式对menu.xml文件进行Pull解析
关键java代码:

private List<String> menu;

private HashMap<String, List<String>> map;

XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
            
InputStream is = getAssets().open("menus.xml");

parser.setInput(is,"utf-8");

int type = parser.getEventType();

while(type != XmlPullParser.END_DOCUMENT){
    switch (type) {
    case XmlPullParser.START_DOCUMENT:
        map = new HashMap<String, List<String>>();
        break;
    case XmlPullParser.START_TAG:
        if("list".equals(parser.getName())){
            menu = new ArrayList<String>();
            name = parser.getAttributeValue(0);
        }else if("food".equals(parser.getName())){
            menu.add(parser.nextText());
        }       
        break;
    case XmlPullParser.END_TAG:
        if ("list".equals(parser.getName())) {
            map.put(name, menu);
        }
        break;
    default:
        break;
    }
    type = parser.next();
    
}

总结

本文介绍了一些主流XML文件解析的方式,分别是DOM解析、SAX解析和PULL解析。因为PULL解析是Google官方推荐的,所以也是我们平时做Android开发最常用的一种。即使这样,我们还是对其他的解析方式及其优缺点有所了解。

  • Dom解析的优点:将整个文档加载到内存中,便于删除、修改、查询的等相关操作。

  • Dom解析的缺点:内存中充满很多无用的节点,浪费资源

  • Sax解析的优点:基于事件驱动解析,无需将整个文档加载到内存中,效率高。

  • Sax解析的缺点:事件过去后,数据没有保存就会丢失。

  • Pull解析:兼容了Dom解析和Sax解析的优点,是谷歌官方推荐使用的Xml解析方式。

推荐阅读更多精彩内容