Dom,pull,Sax解析XML
本篇随笔将详细讲解如何在Android当中解析服务器端传过来的XML数据,这里将会介绍解析xml数据格式的三种方式,分别是DOM、SAX以及PULL。
一、DOM解析XML
我们首先来看看DOM(Document Object Model)这种方式解析xml,通过DOM解析xml在j2ee开发中非常的常见,它将整个xml看成是一个树状的结构,在解析的时候,会将整个xml 文件加载到我们的内存当中,然后通过DOM提供的API来对我们的xml数据进行解析,这种方式解析xml非常的方便,并且我们可以通过某个节点访问到其 兄弟或者是父类、子类节点。那么通过DOM来解析xml的步骤是怎样的呢?
1.首先通过DocumentBuilderFactory这个类来构建一个解析工厂类,通过newInstance()的方法可以得到一个DocumentBuilderFactory的对象。
2.通过上面的这个工厂类创建一个DocumentBuilder的对象,这个类就是用来对我们的xml文档进行解析,通过DocumentBuilderFactory的newDocumentBuilder()方法
3.通过创建好的 DocumentBuilder 对象的 parse(InputStream) 方法就可以解析我们的xml文档,然后返回的是一个Document的对象,这个Document对象代表的就是我们的整个xml文档。
4.得到了整个xml的Document对象后,我们可以获得其下面的各个元素节点(Element),同样每个元素节点可能又有多个属性(Attribute),根据每个元素节点我们又可以遍历该元素节点下面的子节点等等。
在这里要说明一下,在DOM的API当中,Node这个接口代表了我们整个的DOM对象的最初数据类型,它代表了整个document树中的每一个 单一节点。所有实现了Node这个接口的对象都可以处理其孩子节点,当然,并不是每个节点都有children,例如TextNode(文本节点),通过 Node的 nodeName、nodeValue、attributes这三个属性,我们可以很方便的得到每个Node节点的节点名字、节点的值、节点属性等,下面 我们来看看不同类型的Node节点其nodeName、nodeValue、attributes三个属性分别代表的是什么:
Interface | nodeName | nodeValue | attributes |
---|---|---|---|
Attr |
same as Attr.name |
same as Attr.value |
null |
CDATASection |
"#cdata-section" |
same as CharacterData.data , the content of the CDATA Section |
null |
Comment |
"#comment" |
same as CharacterData.data , the content of the comment |
null |
Document |
"#document" |
null |
null |
DocumentFragment |
"#document-fragment" |
null |
null |
DocumentType |
same as DocumentType.name |
null |
null |
Element |
same as Element.tagName |
null |
NamedNodeMap |
Entity |
entity name | null |
null |
EntityReference |
name of entity referenced | null |
null |
Notation |
notation name | null |
null |
ProcessingInstruction |
same as ProcessingInstruction.target |
same as ProcessingInstruction.data |
null |
Text |
"#text" |
same as CharacterData.data , the content of the text node |
null |
其实我们用的最多的就是Element和Text,通过Element的nodeName属性可以得到这个节点的标签名,Text对象的nodeValue得到的就是元素节点的文本值内容,下面我们来看看一个通过DOM解析xml的一个代码案例:
首先我们构建一个xml的文档,这个文档等下会放在我们的服务器上,通过http协议来得到这个xml文档,然后在我们的Android客户端对其进行解析
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="">
<name>小罗</name>
<age></age>
</person>
<person id="">
<name>android</name>
<age></age>
</person>
</persons>
下面我们来看看DOM解析服务器端xml的工具类:
public class DomParserUtils
{
public static List<Person> parserXmlByDom(InputStream inputStream) throws Exception
{
List<Person> persons = new ArrayList<Person>();
// 得到一个DocumentBuilderFactory解析工厂类
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 得到一个DocumentBuilder解析类
DocumentBuilder builder = factory.newDocumentBuilder();
// 接收一个xml的字符串来解析xml,Document代表整个xml文档
Document document = builder.parse(inputStream);
// 得到xml文档的根元素节点
Element personsElement = document.getDocumentElement();
// 得到标签为person的Node对象的集合NodeList
NodeList nodeList = personsElement.getElementsByTagName("person");
for(int i = ; i < nodeList.getLength(); i++)
{
Person person = new Person();
// 如果该Node是一个Element
if(nodeList.item(i).getNodeType() == Document.ELEMENT_NODE)
{
Element personElement = (Element)nodeList.item(i);
// 得到id的属性值
String id = personElement.getAttribute("id");
person.setId(Integer.parseInt(id)); // 得到person元素下的子元素
NodeList childNodesList = personElement.getChildNodes();
for(int j = ; j < childNodesList.getLength(); j++)
{
if(childNodesList.item(j).getNodeType() == Document.ELEMENT_NODE)
{
// 解析到了person下面的name标签
if("name".equals(childNodesList.item(j).getNodeName()))
{
// 得到name标签的文本值
String name = childNodesList.item(j).getFirstChild().getNodeValue();
person.setName(name);
}
else if("address".equals(childNodesList.item(j).getNodeName()))
{
String age = childNodesList.item(j).getFirstChild().getNodeValue();
person.setAge(Integer.parseInt(age));
}
}
} persons.add(person);
person = null;
}
}
return persons;
}
}
通过DOM解析xml的好处就是,我们可以随时访问到某个节点的相邻节点,并且对xml文档的插入也非常的方便,不好的地方就是,其会将整个xml 文档加载到内存中,这样会大大的占用我们的内存资源,对于手机来说,内存资源是非常非常宝贵的,所以在手机当中,通过DOM这种方式来解析xml是用的比 较少的。
二、SAX解析XML
SAX(Simple API for XML),接着我们来看看另一种解析xml的方式,通过sax来对xml文档进行解析。
SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于Android等移动设备。 SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断当前读到的字符是否合法XML 语法中的某部分,如果符合就会触发事件。所谓事件,其实就是一些回调(callback)方法,这些方法(事件)定义在ContentHandler接 口。下面是一些ContentHandler接口常用的方法:
1:startDocument()
当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。 2:endDocument()
和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。 3:startElement(String namespaceURI, String localName, String qName, Attributes atts)
当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。 4:endElement(String uri, String localName, String name)
这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。 5:characters(char[] ch, int start, int length)
这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。
上面提到了重要的一点,sax解析xml是基于事件流的处理方式的,因此每解析到一个标签,它并不会记录这个标签之前的信息,而我们只会知道当前这个表情的名字和它的属性,至于标签里面的嵌套,上层标签的名字这些都是无法知道的。
sax解析xml最重要的步骤就是定义一个我们自己的Handler处理类,我们可以让其继承 DefaultHandler 这个类,然后在里面重写其回调方法,在这些回调方法里来做我们的xml解析
下面我们就通过一个实例来看看如果通过SAX来解析xml,首先定义一个我们自己的Handler类:
public class MyHandler extends DefaultHandler
{
private List<Person> persons;
private Person person;
// 存放当前解析到的标签名字
private String currentTag;
// 存放当前解析到的标签的文本值
private String currentValue; public List<Person> getPersons()
{
return persons;
} // 当解析到文档开始时的回调方法
@Override
public void startDocument() throws SAXException
{
persons = new ArrayList<Person>();
} // 当解析到xml的标签时的回调方法
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException
{
if("person".equals(qName))
{
person = new Person();
// 得到当前元素的属性值
for(int i = ; i < attributes.getLength(); i++)
{
if("id".equals(attributes.getQName(i)))
{
person.setId(Integer.parseInt(attributes.getValue(i)));
}
}
}
// 设置当前的标签名
currentTag = qName;
} // 当解析到xml的文本内容时的回调方法
@Override
public void characters(char[] ch, int start, int length)
throws SAXException
{
// 得到当前的文本内容
currentValue = new String(ch,start, length);
// 当currentValue不为null、""以及换行时
if(currentValue != null && !"".equals(currentValue) && !"\n".equals(currentValue))
{
// 判断当前的currentTag是哪个标签
if("name".equals(currentTag))
{
person.setName(currentValue);
}
else if("age".equals(currentTag))
{
person.setAge(Integer.parseInt(currentValue));
}
}
// 清空currentTag和currentValue
currentTag = null;
currentValue = null;
} // 当解析到标签的结束时的回调方法
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException
{
if("person".equals(qName))
{
persons.add(person);
person = null;
}
}
}
接着看看SAX解析xml的Util类:
public class SaxParserUtils
{
public static List<Person> parserXmlBySax(InputStream inputStream) throws Exception
{
// 创建一个SAXParserFactory解析工厂类
SAXParserFactory factory = SAXParserFactory.newInstance();
// 实例化一个SAXParser解析类
SAXParser parser = factory.newSAXParser();
// 实例化我们的MyHandler类
MyHandler myHandler = new MyHandler();
// 根据我们自定义的Handler来解析xml文档
parser.parse(inputStream, myHandler); return myHandler.getPersons();
}
}
三、PULL解析XML
最后来介绍第三种解析xml的方式,pull。pull解析和sax解析类似,都是基于事件流的方式,在Android中自带了pull解析的jar包,所以我们不需要导入第三方的jar包了。
Pull解析器和SAX解析器的区别:
Pull解析器和SAX解析器虽有区别但也有相似性。他们的区别为:SAX解析器的工作方式是自动将事件推入注册的事件处理器进行处理,因此你不能控制事件的处理主动结束;
而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。这是他们主要的区别。
而他们的相似性在运行方式上,Pull解析器也提供了类似SAX的事件(开始文档START_DOCUMENT和结束文档 END_DOCUMENT,开始元素START_TAG和结束元素END_TAG,遇到元素内容TEXT等),但需要调用next() 方法提取它们(主动提取事件)。
Android系统中和Pull方式相关的包为org.xmlpull.v1,在这个包中提供了Pull解析器的工厂类 XmlPullParserFactory和Pull解析器XmlPullParser,XmlPullParserFactory实例调用 newPullParser方法创建XmlPullParser解析器实例,接着XmlPullParser实例就可以调用getEventType() 和next()等方法依次主动提取事件,并根据提取的事件类型进行相应的逻辑处理。
下面我们就来通过一个代码来看看pull解析xml的步骤:
public class PullParserUtils
{
public static List<Person> parserXmlByPull(InputStream inputStream) throws Exception
{
List<Person> persons = null;
Person person = null; // 创建XmlPullParserFactory解析工厂
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
// 通过XmlPullParserFactory工厂类实例化一个XmlPullParser解析类
XmlPullParser parser = factory.newPullParser();
// 根据指定的编码来解析xml文档
parser.setInput(inputStream, "utf-8"); // 得到当前的事件类型
int eventType = parser.getEventType();
// 只要没有解析到xml的文档结束,就一直解析
while(eventType != XmlPullParser.END_DOCUMENT)
{
switch (eventType)
{
// 解析到文档开始的时候
case XmlPullParser.START_DOCUMENT:
persons = new ArrayList<Person>();
break;
// 解析到xml标签的时候
case XmlPullParser.START_TAG:
if("person".equals(parser.getName()))
{
person = new Person();
// 得到person元素的第一个属性,也就是ID
person.setId(Integer.parseInt(parser.getAttributeValue()));
}
else if("name".equals(parser.getName()))
{
// 如果是name元素,则通过nextText()方法得到元素的值
person.setName(parser.nextText());
}
else if("age".equals(parser.getName()))
{
person.setAge(Integer.parseInt(parser.nextText()));
}
break;
// 解析到xml标签结束的时候
case XmlPullParser.END_TAG:
if("person".equals(parser.getName()))
{
persons.add(person);
person = null;
}
break;
}
// 通过next()方法触发下一个事件
eventType = parser.next();
} return persons;
}
}
最后我们编写一个HttpUtils类来访问我们的服务器端的xml文档:
public class HttpUtils
{
public static InputStream httpMethod(String path, String encode)
{
HttpClient httpClient = new DefaultHttpClient(); try
{
HttpPost httpPost = new HttpPost(path);
HttpResponse httpResponse = httpClient.execute(httpPost);
if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
{
HttpEntity httpEntity = httpResponse.getEntity();
return httpEntity.getContent();
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
httpClient.getConnectionManager().shutdown();
} return null;
}
}
最后来看看我们的Android应用程序的布局文件以及Activity类的代码:
public class MainActivity extends Activity
{
private Button button;
private Button button2;
private Button button3;
private final String PATH = "http://172.25.152.34:8080/httptest/person.xml";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); button = (Button)findViewById(R.id.button1);
button2 = (Button)findViewById(R.id.button2);
button3 = (Button)findViewById(R.id.button3); ButtonOnClickListener listener = new ButtonOnClickListener();
button.setOnClickListener(listener);
button2.setOnClickListener(listener);
button3.setOnClickListener(listener);
} class ButtonOnClickListener implements OnClickListener
{
@Override
public void onClick(View v)
{
Button button = (Button)v;
switch (button.getId())
{
case R.id.button1:
// 启动一个新线程解析xml
class MyThread1 extends Thread
{
@Override
public void run()
{
InputStream inputStream = HttpUtils.httpMethod(PATH, "utf-8");
List<Person> persons = null;
try
{
persons = DomParserUtils.parserXmlByDom(inputStream);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("dom --->>" + persons);
}
}
new MyThread1().start();
break;
case R.id.button2:
// 启动一个新线程解析xml
class MyThread2 extends Thread
{
@Override
public void run()
{
InputStream inputStream = HttpUtils.httpMethod(PATH, "utf-8");
List<Person> persons = null;
try
{
persons = SaxParserUtils.parserXmlBySax(inputStream);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("sax --->>" + persons);
}
}
new MyThread2().start();
break;
case R.id.button3:
// 启动一个新线程解析xml
class MyThread3 extends Thread
{
@Override
public void run()
{
InputStream inputStream = HttpUtils.httpMethod(PATH, "utf-8");
List<Person> persons = null;
try
{
persons = PullParserUtils.parserXmlByPull(inputStream);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("pull: --->>" + persons);
}
}
new MyThread3().start();
break;
}
}
} @Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.main, menu);
return true;
} }
最后我们来看看控制台的输出:
总结:dom方式解析xml,比较简单,并可以访问兄弟元素,但是需要将整个xml文档加载到内存中,对于android设备来说,不推荐使用dom的方式解析xml。
sax和pull都是基于事件驱动的xml解析器,在解析xml时并不会加载整个的xml文档,占用内存较少,因此在android开发中建议使用sax或者pull来解析xml文档。
本文转自http://www.cnblogs.com/xiaoluo501395377/p/3444744.html
Dom,pull,Sax解析XML的更多相关文章
- Android 使用pull,sax解析xml
pull解析xml文件 1.获得XmlpullParser类的引用 这里有两种方法 //解析器工厂 XmlPullParserFactory factory=XmlPullParserFactory. ...
- Java SE之XML<二>XML DOM与SAX解析
[文档整理系列] Java SE之XML<二>XML DOM与SAX解析 XML编程:CRUD(Create Read Update Delete) XML解析的两种常见方式: DOM(D ...
- Android解析xml文件-采用DOM,PULL,SAX三种方法解析
解析如下xml文件 <?xml version="1.0" encoding="UTF-8"?> <persons> <perso ...
- XML - 十分钟了解XML结构以及DOM和SAX解析方式
引言 NOKIA 有句著名的广告语:"科技以人为本".不论什么技术都是为了满足人的生产生活须要而产生的.详细到小小的一个手机.里面蕴含的技术也是浩如烟海.是几千年来人类科技的结晶, ...
- DOM&SAX解析XML
在上一篇随笔中分析了xml以及它的两种验证方式.我们有了xml,但是里面的内容要怎么才能得到呢?如果得不到的话,那么还是没用的,解析xml的方式主要有DOM跟SAX,其中DOM是W3C官方的解析方式, ...
- java基础71 XML解析中的【DOM和SAX解析工具】相关知识点(网页知识)
本文知识点(目录):本文下面的“实例及附录”全是DOM解析的相关内容 1.xml解析的含义 2.XML的解析方式 3.xml的解析工具 4.XML的解析原理 5.实例 6 ...
- schema文件及XML文件的DOM和Sax解析
schema文件 <?xml version="1.0" encoding="UTF-8"?> <schema xmlns="htt ...
- 17.JAVA-Dom、Sax解析XML详解
在JAVA中,解析有三种方式: Dom解析(支持改删,耗内存). Sax解析(不支持改删,不耗内存). Pull解析(在Android中推荐使用的一种解析XML的方式,在下章学习). 1.支持Dom与 ...
- Android之SAX解析XML
一.SAX解析方法介绍 SAX(Simple API for XML)是一个解析速度快并且占用内存少的XML解析器,非常适合用于Android等移动设备. SAX解析器是一种基于事件的解析器,事件驱动 ...
随机推荐
- Java动态代理的两种实现方法
注:文章转载自:https://blog.csdn.net/m0_38039437/article/details/77970633 一.代理的概念 动态代理技术是整个java技术中最重要的一个技术, ...
- func 的参数修饰
1.0 在声明一个 Swift的方法的时候,我们一般不去指定参数前面的修饰符,而是直接声明参数: func incrementor(variable : Int) ->Int { } 这个方法接 ...
- idea git 发起一个pull request 请求
- Android sdk 目录结构说明
1.add-on:附加的包:2.docs:HTML格式的离线文档:3.platforms:sdk核心内容:4.tool:工具. 在platforms中包含了的各个Android SDK版本的目录中,包 ...
- shell 脚本传参
在 shell 中我们会见到 $0.$1.$2这样的符号,这是什么意思呢? 简单来说 $0 就是你写的shell脚本本身的名字,$1 是你给你写的shell脚本传的第一个参数,$2 是你给你写的sh ...
- 递归算法结合数据库 解析 java树形结构
1.准备表结构及对应的表数据a.表结构: create table TB_TREE ( CID NUMBER not null, CNAME VARCHAR2(50), PID NUMBER //父节 ...
- Mysql5.6 导出sql文件数据导入到5.7
由于在linux安装了mysql5.7,在需要导入数据时发现报错,说时间默认值不能为0,因为之前用的是mysql5.6 的版本.经过网上百度查找方法,发现是mysql的sql_mode值的问题,于是就 ...
- Spring设置动态定时任务
1.在Spring中经常会用到定时任务,一般会在业务方法上使用@Schedule(cron="定时执行规则"),无法实现从前台动态设置定时任务. 在java中固定频率的任务使用Sc ...
- sql语句查询菜单结果成 树状图类型 注意适用于id是四位数
select * from ( select pid,id,name,url,concat(id,":") idOrder from menu where pid=0 and st ...
- 解题5(StringMerge1)
题目描述 按照指定规则对输入的字符串进行处理. 详细描述: 将输入的两个字符串合并. 对合并后的字符串进行排序,要求为:下标为奇数的字符和下标为偶数的字符分别从小到大排序.这里的下标意思是字符在字符串 ...