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>

以上是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;
    }

.自定义解析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;
            }

        }

    }

}

.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);

.获取所有的节点

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

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;
    }

}

.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");

. 取得事件类型,根据事件类型判断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();
        }

.对应与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;
    }

}

.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();
    }

.序列化并保存到文件

/**
     * 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());
        }

    }

时间: 2024-08-25 17:35:53

Android开发系列之XML解析的相关文章

Android开发学习---使用XmlPullParser解析xml文件

Android中解析XML的方式主要有三种:sax,dom和pull关于其内容可参考:http://blog.csdn.net/liuhe688/article/details/6415593 本文将主要介绍pull解析器解析xml文件,环境为ubuntu 12.04+ intelij 13.1 + android sdk 2.1 一.创建一个XML项目,步骤如下: 二.解析一个xml文件: assets/person.xml <?xml version="1.0" encodi

Android开发之使用PULL解析和生成XML

请尊重他人的劳动成果,转载请注明出处:Android开发之使用PULL解析和生成XML 一.使用PULL解析XML 1.PULL简介 我曾在<浅谈XMl解析的几种方式>一文中介绍了使用DOM方式,SAX方式,Jdom方式,以及dom4j的方式来解析XML.除了可以使用以上方式来解析XML文件外,也可以使用Android系统内置的Pull解析器来解析XML文件. Pull解析器的运行方式与SAX解析器相似.它提供了类似的事件,如开始元素和结束元素事件.使用parser.next()可以进入下一个

Android开发:碎片Fragment完全解析fragment_main.xml/activity_main.xml

Android开发:碎片Fragment完全解析 为了让界面可以在平板上更好地展示,Android在3.0版本引入了Fragment(碎片)功能,它非常类似于Activity,可以像 Activity一样包含布局.Fragment通常是嵌套在Activity中使用的,现在想象这种场景:有两个 Fragment,Fragment 1包含了一个ListView,每行显示一本书的标题.Fragment 2包含了TextView和 ImageView,来显示书的详细内容和图片. AD:51CTO学院:I

快速Android开发系列网络篇之Retrofit

Retrofit是一个不错的网络请求库,用官方自己的介绍就是: A type-safe REST client for Android and Java 看官网的介绍用起来很省事,不过如果不了解它是怎么实现的也不太敢用,不然出问题了就不知道怎么办了.这几天比较闲就下下来看了一下,了解一下大概实现方法,细节就不追究了.先来看一个官网的例子,详细说明去网官看 简单示例 首先定义请求接口,即程序中都需要什么请求操作 public interface GitHubService { @GET("/use

C#程序员学习Android开发系列之ListView

上篇博客解决了Android客户端通过WebService与服务器端程序进行交互的问题,这篇博客重点关注两个问题,一个是Android应用程序如何与本机文件型数据库SQLite进行交互,另一问题则是如何在ListView中按照我们想要的界面效果进行展示.限于篇幅这篇重点讲ListView,下篇博客重点阐述SQLite. ListView是一个常用的数据显示控件,假设我们要做一个简单的界面,如图所示. 这张图是我直接从Android平板电脑(Android 4.2.2)上面截图下来的,就是一个普通

C#程序员学习Android开发系列之学习路线图

通过前面的3篇博客已经简单的介绍了Android开发的过程并写了一个简单的demo,了解了Android开发的环境以及一些背景知识. 接下来这篇博客不打算继续学习Android开发的细节,先停一下,明确一下接下来的学习目标以及学习路线. 一.对Android开发的基本认识 1.Android原生开发是基于Java语言的,由于我比较擅长C#,所以对Java语言本身不太熟练,需要加强Java语言基础的练习,这一块我会穿插到具体的知识点练习当中,并且在必要的地方给出与C#语言的对比(其实基本上在语法层

快速Android开发系列网络篇之Android-Async-Http

快速Android开发系列网络篇之Android-Async-Http 转:http://www.cnblogs.com/angeldevil/p/3729808.html 先来看一下最基本的用法 AsyncHttpClient client = new AsyncHttpClient(); client.get("http://www.google.com", new AsyncHttpResponseHandler() { @Override public void onSucce

快速Android开发系列网络篇之Volley

Volley是Google推出的一个网络请求库,已经被放到了Android源码中,地址在这里,先看使用方法 RequestQueue mRequestQueue = Volley.newRequestQueue(context); JsonObjectRequest req = new JsonObjectRequest(URL, null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONO

C#程序员学习Android开发系列之Android项目的目录结构

今天开始正式学习Android开发的种种细节,首先从最基本的概念和操作学起. 首先看一下Android项目的目录结构. 这是我随便建立的一个test项目,我们重点关注一下几个方面的内容: 1.src目录:存放java源代码的目录,里面建立一个包,包里面有4个java源文件(分别都继承自Activity).由于java要求比较严格,因此要求类名与文件名一致. gen(Generated Java Files)目录:自动产生Java源文件的目录,是由工具自动生成的,一般不需要自己修改.里面主要有一个