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