Android开发系列之XML解析

  xml文件存储是常用的数据存储方式,xml解析常用的有SAX解析、DOM解析、PULL解析等。本篇讲述xml的格式,xml的写入方式以及xml的解析。

  .xml格式

技术分享
<cartons>
  <carton id="1">
    <name>天空之城</name>
    <maker>宫崎骏</maker>
    <style>冒险</style>
    <date>1986年8月2日</date>
  </carton>

  <carton id="2">
    <name>哈尔的移动城堡</name>
    <maker>宫崎骏</maker>
    <style>奇幻</style>
    <date>2004年11月20日</date>
  </carton>

  <carton id="3">
    <name>千与千寻</name>
    <maker>宫崎骏</maker>
    <style>生活</style>
    <date>2001年7月20日</date>
  </carton>
</cartons>
View Code

  以上是xml存储形式的一般格式,现在针对以上xml文件进行解析。

 .SAX解析

   SAX解析器是一种基于事件的解析器,它采用事件驱动的流式解析方式,它的核心是事件处理模式。SAX的解析规则是顺序地从文件开始解析到结尾,不可回退。

   SAX的工作原理:对文档进行顺序扫描,当扫描到文档(document)开始与结束Tag、元素(element)开始与结束Tag等时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

   SAX解析的流程:

   .SAX解析调用

技术分享
/**
     * SAX解析器解析
     */
    public List<Carton> parseSAX(InputSource input) {

        try {
            // 创建SAX解析器
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();
            XMLReader reader = parser.getXMLReader();

            // 开始解析
            SAXContentHandler handler = new SAXContentHandler();
            reader.setContentHandler(handler);
            reader.parse(input);

            return handler.getParserResult();

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
View Code

  .自定义解析Handler

    .继承DefaultHandler

    .重写以下5个方法

      . startDocument 开始解析文档,一般在这里作必要的初始化

      . endDocument 结束解析文档

      . startElement 开始解析节点,XML解析器遇到tag时调用。参数描述:

                           1、uri 命名空间

                           2、localName 不带命名空间前缀的标签名称

                           3、qName 带有命名空间前缀的标签名称

                           4、attributes 元素的属性集合

      . endElement 结束解析节点,XML解析器遇到结束tag时调用。

      . characters 处理读取到的节点信息,参数解析:

                           1、char[] ch xml文件内容

                           2、int start 读取节点在char[]中的初始位置

                           3、int length 读取内容的长度

                           一般通过 String str = new String(ch, start, length)来获取某一节点的内容。

     Handler代码:

技术分享
public class SAXContentHandler extends DefaultHandler {

    public List<Carton> datas;
    public Carton carton;

    public String curr_target;

    // ////////////XML待解析标签

    public static final String elem_carton = "carton";
    public static final String value_id = "id";
    public static final String elem_name = "name";
    public static final String elem_maker = "maker";
    public static final String elem_style = "style";
    public static final String elem_date = "date";

    public List<Carton> getParserResult() {
        return datas;
    }

    /**
     * 开始解析,一般在这里进行初始化
     */
    @Override
    public void startDocument() throws SAXException {
        datas = new ArrayList<Carton>();
    }

    /**
     * 结束解析,一般在这里进行资源的释放
     */
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }

    /**
     * XML解析器顺序解析到tag时调用
     * 
     * @param uri
     *            命名空间
     * @param localName
     *            不带命名空间前缀的标签名称
     * @param qName
     *            带有命名空间前缀的标签名称
     * @param attributes
     *            元素的属性集合
     */
    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {

        Log.d("", "-----startElement Tag = " + localName);
        
        if (localName.equals(elem_carton)) {
            // 父标签 carton
            carton = new Carton();
            carton.id = Integer.parseInt(attributes.getValue(value_id));
            // carton.id = Integer.parseInt(attributes.getValue(0));
        }

        curr_target = localName;
    }

    /**
     * XML解析器遇到结束标签时,调用该方法
     */
    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {

        if (elem_carton.equals(localName)) {
            datas.add(carton);
            carton = null;
        }
        // 解析结束时标签置空,很重要!
        curr_target = null;
    }

    /**
     * 处理在xml中读到的内容
     * 
     * @param ch
     *            文件内容
     * @param start
     *            内容在数组的初始位置
     * @param length
     *            内容在数组的长度
     */
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {

        Log.d("", "-----characters currTag = " + curr_target);
        
        if (curr_target != null) {
            // 取出当前标签的内容
            String content = new String(ch, start, length);
            // 取出子标签的属性值
            if (curr_target.equals(elem_name)) {
                carton.name = content;
            } else if (curr_target.equals(elem_maker)) {
                carton.author = content;
            } else if (curr_target.equals(elem_style)) {
                carton.style = content;
            } else if (curr_target.equals(elem_date)) {
                carton.date = content;
            }

        }

    }

}
View Code

.DOM解析

  Dom解析xml文件,是先将xml文件一次性装载到内存,然后通过DOM API来遍历xml树,检索需要的数据,Dom解析规范的核心是树模型。

  Dom解析的流程:

  .装载xml文件

技术分享
// 创建DocumentBuilder加载xml文件
            DocumentBuilderFactory factory = DocumentBuilderFactory
                    .newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(input);
View Code

  .获取所有的节点

  .根据节点,取节点的子节点

技术分享
public class DOMIParser {

    private static final String TAG = "DOMIParser";

    // 节点
    public String rootElem = "cartons";
    public String element = "carton";

    // 属性字段
    public static final String value_id = "id";
    public static final String elem_name = "name";
    public static final String elem_maker = "maker";
    public static final String elem_style = "style";
    public static final String elem_date = "date";

    /**
     * DOM解析器解析
     * 
     * @param input
     * @return
     */
    public List<Carton> parseDom(InputSource input) {

        List<Carton> datas = new ArrayList<Carton>();
        Carton data = null;

        try {
            // 创建DocumentBuilder加载xml文件
            DocumentBuilderFactory factory = DocumentBuilderFactory
                    .newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(input);

            // 获取DOM树,取得所有的element节点列表
            Element rootElem = document.getDocumentElement();
            NodeList elemList = rootElem.getElementsByTagName(element);

            int len = elemList.getLength();
            for (int i = 0; i < len; i++) {
                data = new Carton();
                // 取出第i个element节点
                Element node = (Element) elemList.item(i);
                data.id = Integer.parseInt(node.getAttribute(value_id));
                // 获取element节点下的所有子节点
                NodeList childNodes = node.getChildNodes();

                int childlen = childNodes.getLength();
                for (int j = 0; j < childlen; j++) {
                    Node childNode = childNodes.item(j);
                    // 判断Node是否为元素类型
                    if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                        Element childElem = (Element) childNode;

                        if (childElem.getNodeName().equals(elem_name)) {
                            data.name = childElem.getFirstChild()
                                    .getNodeValue();
                        } else if (childElem.getNodeName().equals(elem_maker)) {
                            data.author = childElem.getFirstChild()
                                    .getNodeValue();
                        } else if (childElem.getNodeName().equals(elem_style)) {
                            data.style = childElem.getFirstChild()
                                    .getNodeValue();
                        } else if (childElem.getNodeName().equals(elem_date)) {
                            data.date = childElem.getFirstChild()
                                    .getNodeValue();
                        }
                    }
                }

                datas.add(data);
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return datas;
    }

}
View Code

.DOM与SAX特点

 DOM:

          . DOM是基于内存的。

          . DOM解析要把xml文件全部装载到内存,因此需要耗费很大的内存空间;

          . DOM可以对其中的元素进行随机访问

          . DOM可以向xml文件插入数据,具有可修改性 

 SAX:  

         . SAX是基于事件的。

         . SAX不需要加载全部文件,且按顺序每次只解析一个节点,因此只耗费很少的内存空间;

         . SAX从头到尾顺序访问各个元素,且只遍历一次

         . SAX只能读取xml数据,不能修改 

      从上述可见,DOM在处理XML文件时,将XML文件解析成树状结构并放入内存中进行处理。当XML文件较小时,我们可以选DOM,因为它简单、直观。SAX则是以事件作为解析XML文件的模式,它将XML文件转化成一系列的事件,XML文件较大时,选择SAX技术是比较合理的。

.PULL解析

      Pull解析是Android第三方开发的开源技术,同样也适用于JabaSE开发。Pull解析解析速度快,简单易用,使用效率非常高。Pull解析的整个过程类似于SAX解析,Pull不再使用解析回调的方式,而是以一个整数的返回来判定解析到了哪个标签,使用起来非常灵活。

Pull解析的流程:

. 创建一个Pull解析器,并加载xml文件

技术分享
// 创建一个Pull解析对象
XmlPullParserFactory factory =  XmlPullParserFactory.newInstance();     
XmlPullParser parser = factory.newPullParser();
parser.setInput(in, "UTF-8");
View Code

. 取得事件类型,根据事件类型判断startDocument、endDocument、startTag、endTag

技术分享
int eventType = parser.getEventType();
        // 类似于SAX,解析截止到 END_DOCUMENT
        while (eventType != XmlPullParser.END_DOCUMENT) {
          
            switch (eventType) {
            // 文档开始事件(开始解析)
            case XmlPullParser.START_DOCUMENT:
                break;
            // 标签开始事件(开始解析tag)
            case XmlPullParser.START_TAG:
                break;
            // 标签结束事件(解析tag结束)
            case XmlPullParser.END_TAG:
                break;
            }

            eventType = parser.next();
        }
View Code

.对应与SAX解析的四个重写方法,读取数据并保存

Pull解析类代码如下:

技术分享
public class PULLIParser implements XmlParser {

    private static final String TAG = "PULLIParser";

    // 节点
    public String rootElem = "cartons";
    public String element = "carton";

    // 属性字段
    public static final String value_id = "id";
    public static final String elem_name = "name";
    public static final String elem_maker = "maker";
    public static final String elem_style = "style";
    public static final String elem_date = "date";

    @Override
    public List<Carton> parse(InputStream in) throws Exception {

        List<Carton> datas = new ArrayList<Carton>();
        Carton data = null;

        // 创建一个Pull解析对象
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        XmlPullParser parser = factory.newPullParser();
        parser.setInput(in, "UTF-8");

        int eventType = parser.getEventType();
        // 类似于SAX,解析截止到 END_DOCUMENT
        while (eventType != XmlPullParser.END_DOCUMENT) {
          
            switch (eventType) {
            // 文档开始事件(开始解析)
            case XmlPullParser.START_DOCUMENT:

                datas = new ArrayList<Carton>();
                break;
            // 标签开始事件(开始解析tag)
            case XmlPullParser.START_TAG:
                  Log.w(TAG, parser.getName());
                if (parser.getName().equals(element)) {

                    data = new Carton();
                    data.id = Integer.parseInt(parser.getAttributeValue(0));

                } else if (parser.getName().equals(elem_name)) {

                    data.name = parser.nextText();

                } else if (parser.getName().equals(elem_maker)) {

                    data.author = parser.nextText();

                } else if (parser.getName().equals(elem_style)) {

                    data.style = parser.nextText();

                } else if (parser.getName().equals(elem_date)) {

                    data.date = parser.nextText();

                }
                break;
            // 标签结束事件(解析tag结束)
            case XmlPullParser.END_TAG:

                if (parser.getName().equals(element)) {
                    datas.add(data);
                    data = null;
                }
                break;
            }

            eventType = parser.next();
        }

        return datas;
    }

    @Override
    public <T> String serialize(List<T> datas) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

}
View Code

 

.Xml序列化

  在开发中,我们往往不只是将Xml解析出来,有时我们需要将实体类对象的集合以Xml的格式存储到本地,就需要将实体类集合序列化。

  .序列化为String

技术分享
/**
     * Xml序列化(将实体类集序列化为Xml字符串)
     * 
     * @param datas
     * @return
     * @throws Exception
     */
    public static String serialize(List<Carton> datas) {

        if (datas == null) {
            return null;
        }

        StringWriter writer = null;
        try {
            // 创建XmlSerializer对象
            XmlSerializer serializer = Xml.newSerializer();
            writer = new StringWriter();
            serializer.setOutput(writer);

            serializer.startDocument("UTF-8", true);
            serializer.startTag(namespace, root);

            for (Carton carton : datas) {

                // 添加父标签,并写入属性id <carton id = "1">
                serializer.startTag(namespace, element);
                serializer.attribute(namespace, value_id, carton.id + "");

                serializer.startTag(namespace, elem_name);
                serializer.text(carton.name);
                serializer.endTag(namespace, elem_name);

                serializer.startTag(namespace, elem_maker);
                serializer.text(carton.author);
                serializer.endTag(namespace, elem_maker);

                serializer.startTag(namespace, elem_style);
                serializer.text(carton.style);
                serializer.endTag(namespace, elem_style);

                serializer.startTag(namespace, elem_date);
                serializer.text(carton.date);
                serializer.endTag(namespace, elem_date);

                serializer.endTag(namespace, element);
            }

            serializer.endTag(namespace, root);
            serializer.endDocument();
            
        } catch (Exception e) {
            Log.e(TAG, "Xml序列化异常! " + e.getMessage());
        }
        
        return writer.toString();
    }
View Code

  .序列化并保存到文件

技术分享
/**
     * Xml序列化(将实体类集序列化到文件)
     * 
     * @param datas
     * @param file
     */
    public static void serialize(List<Carton> datas, File file) {

        if (datas == null || file == null) {
            return;
        }

        if (!file.exists()) {
            return;
        }

        try {
            // 创建XmlSerializer对象
            XmlSerializer serializer = Xml.newSerializer();
            FileOutputStream fos = new FileOutputStream(file);
            serializer.setOutput(fos, "UTF-8");

            serializer.startDocument("UTF-8", true);
            serializer.startTag(namespace, root);

            for (Carton carton : datas) {

                // 添加父标签,并写入属性id <carton id = "1">
                serializer.startTag(namespace, element);
                serializer.attribute(namespace, value_id, carton.id + "");

                serializer.startTag(namespace, elem_name);
                serializer.text(carton.name);
                serializer.endTag(namespace, elem_name);

                serializer.startTag(namespace, elem_maker);
                serializer.text(carton.author);
                serializer.endTag(namespace, elem_maker);

                serializer.startTag(namespace, elem_style);
                serializer.text(carton.style);
                serializer.endTag(namespace, elem_style);

                serializer.startTag(namespace, elem_date);
                serializer.text(carton.date);
                serializer.endTag(namespace, elem_date);

                serializer.endTag(namespace, element);
            }

            serializer.endTag(namespace, root);
            serializer.endDocument();
            
        } catch (Exception e) {
            Log.e(TAG, "Xml序列化异常! " + e.getMessage());
        }

    }
View Code

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。