转载:http://blog.csdn.net/feng88724/article/details/7013675
在讲这次错误之前,先看一下下面这段代码。 【◆以下解析方法是错误的×】
- import java.util.ArrayList;
- import java.util.List;
- import org.xml.sax.Attributes;
- import org.xml.sax.SAXException;
- import org.xml.sax.helpers.DefaultHandler;
- import android.util.Log;
- public class XmlHandler extends DefaultHandler{
- private final String TAG = this.getClass().getSimpleName();
- /**XML文件中标签定义*/
- private final String TAG_Article = "Article";
- private final String TAG_ArticleID = "ArticleID";
- private final String TAG_Title = "Title";
- private final String TAG_Date = "Date";
- private final String TAG_SmallPictures = "SmallPictures";
- private final String TAG_LargePictures = "LargePictures";
- private final String TAG_Category = "Category";
- private static final String TAG_HeadNote = "HeadNote";
- private static final String TAG_SubTitle = "SubTitle";
- private static final String TAG_Source = "Source";
- //当前正在解析的TAG
- private String currentName;
- //单个文章
- private News news = null;
- //文章列表
- private List<News> newsList = null;
- //解析开始时间
- private long start_time;
- private boolean flag = false;
- @Override
- public void characters(char[] ch, int start, int length)
- throws SAXException {
- super.characters(ch, start, length);
- if(!flag) {
- return;
- }
- // 取值
- String value = new String(ch, start, length);
- Log.d(TAG, "Element: " + currentName + " Element Value: " + value);
- if(value != null) {
- if(TAG_ArticleID.equals(currentName)) {
- news.setArticleId(value);
- } else if(TAG_Title.equals(currentName)) {
- news.setTitle(value);
- } else if(TAG_Date.equals(currentName)) {
- news.setDate(value);
- } else if(TAG_Category.equals(currentName)) {
- news.setCategory(value);
- } else if(TAG_SmallPictures.equals(currentName)) {
- news.setSmallPicture(value);
- } else if(TAG_LargePictures.equals(currentName)) {
- news.setLargePicture(value);
- } else if(TAG_HeadNote.equals(currentName)) {
- news.setHeadNote(value);
- } else if(TAG_SubTitle.equals(currentName)) {
- news.setSubTitle(value);
- } else if(TAG_Source.equals(currentName)) {
- news.setSource(value);
- }
- }
- }
- @Override
- public void startDocument() throws SAXException {
- super.startDocument();
- start_time = System.currentTimeMillis();
- newsList = new ArrayList<News>();
- }
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
- super.startElement(uri, localName, qName, attributes);
- this.currentName = localName;
- flag = true;
- if(TAG_Article.equals(localName)) {
- news = new News();
- }
- }
- @Override
- public void endElement(String uri, String localName, String qName)
- throws SAXException {
- super.endElement(uri, localName, qName);
- flag = false;
- if(TAG_Article.equals(localName)) {
- newsList.add(news);
- }
- }
- @Override
- public void endDocument() throws SAXException {
- super.endDocument();
- long end = System.currentTimeMillis();
- Log.d(TAG, "Parse List's Xml Cost: " + (end - start_time) + " !!");
- }
- }
Baidu 或者 Google 一下 “Android Sax 解析” , 给出的Sample无一例外都是如此。 坑爹啊... 甚至连有些书籍中都是这么写的, 比如《Android开发入门与实践》。(本书亲自确认过,其他书情况不详)
没错, 一般情况下,这么写是可以的, 而且在大多数情况下解析出来也是正确的。 但是就是偶尔会出错, 这个时候通常你都莫不着头脑, 怎么回事? 数据没错啊,解析部分代码貌似也没问题.. 真是奇了怪了。 其实问题都出在上面那段代码上!!
大家都认为 SAX 解析过程大致如下:
startDocument -> startElement -> characters -> endElement -> endDocument
没错,就是这样, startElement 读取起始标签, endElement 读取结束标签,characters 呢?当然是读取其值, 这没错,但是大家都天真的以为 characters 只执行一次,并且一次就读取了全部内容。错就错在这!
其实characters 是很有可能会执行多次的,当遇到内容中有回车,\t等等内容时,它很有可能就执行多次。 有的人可能会说,那我没有这些是不是就只执行一次了? 看下我实测结果:
测试用XML如下:
- <News>
- <Article>
- <ArticleID>1000555</ArticleID>
- <Title><![CDATA[ 郑州“亚洲第一桥”通车6年成危桥 ]]></Title>
- <Date>2011-11-25 14:23:52</Date>
- <SmallPictures>livenews/images/s20.png</SmallPictures>
- <LargePictures>livenews/images/l20.png</LargePictures>
- <Category>闻天下</Category>
- <HeadNote></HeadNote>
- <SubTitle></SubTitle>
- <Author></Author>
- <Source>人民日报</Source>
- <Abstract></Abstract>
- </Article>
- <Article>
- <ArticleID>1000554</ArticleID>
- <Title><![CDATA[ 内地事业单位拟设统一工资制度 ]]></Title>
- <Date>2011-11-25 14:22:33</Date>
- <Category><![CDATA[ 闻天下 ]]></Category>
- <HeadNote></HeadNote>
- <SubTitle></SubTitle>
- <Author></Author>
- <Source></Source>
- <Abstract></Abstract>
- </Article>
- <Article>
- <ArticleID>1000553</ArticleID>
- <Title></Title>
- <Date>2011-11-25 14:21:23</Date>
- <SmallPictures>livenews/images/s21.png</SmallPictures>
- <LargePictures>livenews/images/l21.png</LargePictures>
- <Category><![CDATA[ 星娱乐 ]]></Category>
- <HeadNote></HeadNote>
- <SubTitle></SubTitle>
- <Author></Author>
- <Source><![CDATA[ 凤凰网综合 ]]></Source>
- <Abstract></Abstract>
- </Article>
- <News>
可以很明显的看到,在解析 <ArticleID>1000553</ArticleID> 这一段时, characters执行了两次,将内容"1000553"分两次读取.. 用上面那种方式的最终结果就是 ArticleID = 00553 了。 那如果你的应用需要根据这个id 进一步获取内容岂不是死翘翘了?(比如这边根据id获取新闻详细内容)
好了,废话不多说了,看下正确的写法! 【★以下解析方法才是正确的 √ 】
- import java.util.ArrayList;
- import java.util.List;
- import org.xml.sax.Attributes;
- import org.xml.sax.SAXException;
- import org.xml.sax.helpers.DefaultHandler;
- import android.util.Log;
- public class XmlHandler extends DefaultHandler{
- private final String TAG = this.getClass().getSimpleName();
- /**XML文件中标签定义*/
- private final String TAG_Article = "Article";
- private final String TAG_ArticleID = "ArticleID";
- private final String TAG_Title = "Title";
- private final String TAG_Date = "Date";
- private final String TAG_SmallPictures = "SmallPictures";
- private final String TAG_LargePictures = "LargePictures";
- private final String TAG_Category = "Category";
- private static final String TAG_HeadNote = "HeadNote";
- private static final String TAG_SubTitle = "SubTitle";
- private static final String TAG_Source = "Source";
- //单个文章
- private News news = null;
- //文章列表
- private List<News> newsList = null;
- //解析开始时间
- private long start_time;
- //(1)
- private StringBuilder sb = new StringBuilder();
- @Override
- public void characters(char[] ch, int start, int length)
- throws SAXException {
- super.characters(ch, start, length);
- //(2)不管在startElement到endElement的过程中,执行了多少次characters, 都会将内容添加到StringBuilder中,不会丢失内容
- sb.append(ch, start, length);
- }
- @Override
- public void startDocument() throws SAXException {
- super.startDocument();
- start_time = System.currentTimeMillis();
- newsList = new ArrayList<News>();
- }
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
- super.startElement(uri, localName, qName, attributes);
- //(3) 开始收集新的标签的数据时,先清空历史数据
- sb.setLength(0);
- if(TAG_Article.equals(localName)) {
- news = new News();
- }
- }
- @Override
- public void endElement(String uri, String localName, String qName)
- throws SAXException {
- super.endElement(uri, localName, qName);
- //(4)原来在characters中取值,现改在此取值
- String value = sb.toString();
- if(TAG_ArticleID.equals(localName)) {
- news.setArticleId(value);
- } else if(TAG_Title.equals(localName)) {
- news.setTitle(value);
- } else if(TAG_Date.equals(localName)) {
- news.setDate(value);
- } else if(TAG_Category.equals(localName)) {
- news.setCategory(value);
- } else if(TAG_SmallPictures.equals(localName)) {
- news.setSmallPicture(value);
- } else if(TAG_LargePictures.equals(localName)) {
- news.setLargePicture(value);
- } else if(TAG_HeadNote.equals(localName)) {
- news.setHeadNote(value);
- } else if(TAG_SubTitle.equals(localName)) {
- news.setSubTitle(value);
- } else if(TAG_Source.equals(localName)) {
- news.setSource(value);
- }
- if(TAG_Article.equals(localName)) {
- newsList.add(news);
- }
- }
- @Override
- public void endDocument() throws SAXException {
- super.endDocument();
- long end = System.currentTimeMillis();
- Log.d(TAG, "Parse List's Xml Cost: " + (end - start_time) + " !!");
- }
- }
归纳为三点:
1.startElement的时候, new StringBuilder(); 或者 sb.setLength(0); (我建议后者)
2.characters的时候,sb.append(ch, start, length);
3.endElement的时候,sb.toString(); 此时StringBuilder中的内容才是解析的结果
通过这种方法就不会再有数据离奇丢失的情况了(同时也不需要像错误方法那样再设个currentTag之类的了,逻辑繁杂了,还出错)!
希望大家可以尽早看到这篇文章,不要继续被吭了!!!
相关推荐
在Android中,我们可以使用`DefaultHandler`作为SAX解析器的基类,创建一个继承自`DefaultHandler`的自定义处理器类,然后重写其中的方法,如`startElement`、`endElement`和`characters`,来处理XML中的元素和内容...
本篇文章将详细介绍如何在Android环境中使用SAX解析器来处理从网络获取的XML文件。 1. **XML与SAX解析基础** - XML是一种结构化的文本数据表示方式,它定义了标签、属性等规则,使数据具有自解释性。 - SAX解析器...
通过SaxXmlDemo这个示例项目,你可以看到如何将以上步骤结合到实际应用中,运行示例以了解SAX解析的完整流程。通过学习和实践,你可以掌握在Android中使用SAX解析XML的技能,提升数据处理的能力。
在Android项目中,无需额外安装库,因为Android SDK已经内置了SAX解析器。只需要在代码中创建`SAXParserFactory`,然后使用`newSAXParser()`方法生成`SAXParser`实例。 ```java import javax.xml.parsers.SAXParser...
在Android开发中,获取并解析网络上的数据是常见的任务,特别是获取天气预报信息。Google提供了天气API服务...总的来说,SAX解析器提供了一种高效且灵活的方式来处理XML数据,是Android开发中处理网络数据的有力工具。
SAX(Simple API for XML)是一种事件驱动的解析器,它不将整个XML文档加载到内存中,而是逐行读取,逐个事件处理,因此对于大体积的XML文件,SAX解析相比DOM解析更为高效。本篇文章将详细介绍如何在Android中使用...
下面我们将详细讨论如何在Android中使用SAX解析XML。 首先,我们需要了解SAX解析的基本原理。SAX解析器在读取XML文档时,会触发一系列的事件,如开始文档、开始元素、结束元素、字符数据等。开发者需要定义一个...
以下是使用Android中的SAX解析XML文件的关键步骤: 1. **创建解析器**: 首先,需要创建一个`SAXParserFactory`实例,然后通过`newSAXParser()`方法获取`SAXParser`对象。 ```java SAXParserFactory factory = ...
在Android项目中,我们不需要额外引入SAX库,因为Android SDK已经内置了SAX解析支持。只需使用`org.xml.sax.*`包下的类即可。 ### 三、创建SAX解析器的配置及事件处理器 1. 首先,我们需要创建一个`DefaultHandler...
本示例"android_sax_xml.zip"显然是一个关于在Android平台上使用SAX解析XML的实例。 SAX解析器遵循事件驱动模型,它读取XML文档并触发一系列的回调方法,当遇到如元素开始、结束、字符数据等结构时。这种模式使得...
在Android开发中,XML(可扩展标记语言)作为一种数据存储和交换格式,广泛应用于应用程序的配置、数据存储以及网络...学习和掌握SAX解析是提升Android开发技能的重要一环,它能帮助你在处理XML数据时更加高效和灵活。
为了提高用户体验,我们还需要考虑错误处理,比如文件不存在、解析错误等情况。 总结,Android下解析XML文件涉及的关键知识点包括: - XML的基本概念和结构 - DOM解析器的工作原理和使用方法 - SAX解析器的工作原理...
2. **非回溯性**:SAX解析器一旦读过一个节点,就不会返回去检查或修改它,因此不适合需要前后文信息的解析场景。 3. **顺序解析**:SAX解析器按照XML文档的顺序逐个处理节点。 **二、SAX解析步骤** 1. **创建...
综上所述,Android应用可以借助SAX解析器高效地处理XML数据,实现与后台的XML交互。在实际项目中,我们还需要考虑错误处理、网络连接管理、数据缓存等问题,以确保应用的稳定性和用户体验。通过学习和实践,开发者...
本篇文章将详细介绍如何利用SAX解析器来读取XML文件内容,这是一种轻量级、事件驱动的解析方式,特别适合处理大文件。 SAX(Simple API for XML)解析器在读取XML文件时,不会一次性加载整个文件到内存,而是逐行或...
SAX解析器采用事件驱动模型,逐行读取XML文档,遇到元素、属性等事件时触发回调函数。这种方式节省内存,适合处理大文件,但编程相对复杂,需要编写事件处理代码。 3. **Pull解析器(PULL Parsing)** Pull解析器...
2. **SAX解析**: 通过创建一个实现了`DefaultHandler`接口的类,重写其中的方法来处理解析事件。例如: ```java public class MySAXHandler extends DefaultHandler { private String currentTag; @Override...
- 在处理XML事件时,一定要注意异常处理,如文件不存在、解析错误等情况。 - 对于嵌套标签,确保在开始标签和结束标签之间正确地处理数据。 - Pull解析器不支持命名空间(除非在创建时设置了Namespace感知),如果...
而SAX解析采用事件驱动模型,逐行读取XML,适用于处理大型XML文件。本文将重点介绍SAX解析的一种实现方式——Pull解析。 Pull解析器(XMLPullParser)是Android SDK提供的一种轻量级、高效的XML解析方式,它不需要...
在Android中使用SAX解析XML: 1. 引入依赖:在项目中引入相应的解析库,如Android SDK自带的`javax.xml.parsers`包。 2. 创建XML文件:XML文件可以存储在项目的资源目录或者外部存储。 3. 编写解析逻辑:实现`...