XML解析有传统的dom方法还有Jsoup,SAX,PULL等,这里讲的是比较省内存的SAX和PULL方法。Android中极力推荐用PULL的方式来解析,我个人觉得pull确实比较简单,但其内部的逻辑性不是很分明。所以今天做了个类来将其中的多个步骤进行了分割,以后直接拿来用即可。
1.SAX:
首先先讲解SAX中各个方法的作用:
我们以这个不规则的xml语句做例子:
jack
startDocument:开始解析一个xml文件时触发
endDocument:这个xml文件被解析完毕时触发
startElement:开始解析xml文件中的一个标签时触发,这里可以得到标签名和其中的各个属性值。
如:从会得到标签名:【person】和属性值:【age = 12 sex = f】
endElement:结束解析一个标签时触发
characters:解析这个标签内部的内容时触发,这里可以得到这个标签子节点中的内容。
如:从jack中得到【jack】
下面是实现代码:
1.首先建立一个SAX对象,然后进行解析工作。这里会要自己建立一个ContentHandler的子类
/*** 通过sax进行解析
*@paramstr*/
public voidsax(String str) {//下面是固定的写法
SAXParserFactory factory =SAXParserFactory.newInstance();
XMLReader reader;try{//得到xmlReader对象
reader =factory.newSAXParser().getXMLReader();//设置内容处理器
reader.setContentHandler(newMyContentHandler());
reader.parse(new InputSource(newStringReader(str)));
}catch (SAXException | ParserConfigurationException |IOException e) {//TODO 自动生成的 catch 块
e.printStackTrace();
}
}
MyContentHandler.java 这个类就是来处理事务的,里面有各种回调方法
packagecom.kale.xml;importorg.xml.sax.Attributes;importorg.xml.sax.SAXException;importorg.xml.sax.helpers.DefaultHandler;/***@author:Jack Tony
* @tips :举个极端的例子:kale
* startElement中可以得到的是:type="hidden" name="UserType"
* characters中得到的是:kale
* @date :2014-10-11*/
public class MyContentHandler extendsDefaultHandler{//当前正在解析的标签名
privateString currentTag;/** 开始解析这个xml文件的时候触发*/@Overridepublic void startDocument() throwsSAXException {
System.out.println("开始解析这个文件了");
}/** 结束解析这个xml文件的时候触发*/@Overridepublic void endDocument() throwsSAXException {
System.out.println("文件解析结束");
}/** 开始解析每个元素的时候触发
*
* jack
* 1.uri:当前正在解析的元素的命名空间
* 2.localName:不带前缀的这个元素的名字——>name
* 3.qName:带前缀的这个元素命——>kale:name
* 4.attributes:得到的元素中的属性——>age=12 set=f*/@Overridepublic voidstartElement(String uri, String localName, String qName,
Attributes attributes)throwsSAXException {//举例:
currentTag = localName;//input
System.out.println("————开始解析"+qName+"这个标签了————");for (int i = 0; i < attributes.getLength(); i++) {
String name= attributes.getLocalName(i);//第一次是:type
String value = attributes.getValue(i);//第一次是:hidden
System.out.println(name + " = " +value);
}
}/*(非 Javadoc)
* @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
* 停止解析这个元素的时候触发*/@Overridepublic voidendElement(String uri, String localName, String qName)throwsSAXException {//TODO 自动生成的方法存根
super.endElement(uri, localName, qName);
System.out.println("————-解析"+qName+"标签结束————");
}/** 得到元素中的内容,比如下面的jack
* jack*/@Overridepublic void characters(char[] ch, int start, intlength)throwsSAXException {//举例:jack
if (currentTag.equals("name")) {
System.out.println("name = " + new String(ch,start,length));//会输出jack
}if (currentTag.equals("age")) {
System.out.println("age = " + new String(ch,start,length));//会输出21
}
}
}
贴上测试样本(由于xml文件可能是不规范的,所以处理时要考虑异常):
jack
21
测试结果:
2.PULL
其实PULL中就一个重要的方法XmlPullParser.next();,正因如此才让其变得简单很多。PULL的特点是运行到什么状态是没有回调方法的,它进行某个处理状态时,会改变一个状态变量,通过getEventType()就可以来判断当前是处于什么状态了。
推荐浏览:http://384444165.iteye.com/blog/1521332
但正因为内部逻辑需要开发者来处理,所以变得结构不是很清晰。我这里通过一个类来将其转换为SAX的框架,以后只需要复写这些方法便可以直接进行操作了。至于运行到哪一步,看方法名酒明白了。同样还是之前的那幅图:
这里面的方法的作用也是和SAX一样的。下面是使用的代码:
1.建立这个类的对象,执行操作
/*** 通过pull进行解析
*@paramstr*/
public voidpull(String str) {
XmlPullParser parser=Xml.newPullParser();
InputStream in= newByteArrayInputStream(str.getBytes());try{
parser.setInput(in,"utf-8");
MyXmlPullParserTask task= newMyXmlPullParserTask(parser);
task.execute();//开始解析文件
} catch(XmlPullParserException e) {
e.printStackTrace();
}
}
2.进行解析处理
MyXmlPullParserTask.java
我通过前面的switch-case语句将处理的状态进行了分割,这些状态可以完全类比到SAX中。这样便于理解!
packagecom.kale.xml;importjava.io.IOException;importorg.xmlpull.v1.XmlPullParser;importorg.xmlpull.v1.XmlPullParserException;public classMyXmlPullParserTask {/*** 当前正在解析的标签名,类似于sax中的localName
* 可以得到jack中【kale】*/
privateString currentTag;/*** 当前正在解析的标签名的前缀,sax中的qName=前缀+当前标签名
* 可以得到jack中【abc】*/
privateString currentPrefix;/*** 当前正在解析的标签的命名空间,类似于sax中的uri
* 得到
*
* jack
* 中的【http://schemas.android.com/apk/res/android】*/
privateString currentNamespace;privateXmlPullParser parser;publicMyXmlPullParserTask(XmlPullParser parser) {this.parser =parser;
}/*** 开始解析的方法,这里已经写好了,尽量不要该这里的代码。*/
public voidexecute() {try{//得到当前状态的标识代码
int eventCode =parser.getEventType();//如果当前状态不是文档结束,那么就继续循环
boolean flag = true;while(flag) {//当前解析元素的标签,不带前缀
currentTag =parser.getName();
currentNamespace=parser.getNamespace();
currentPrefix=parser.getPrefix();switch(eventCode) {caseXmlPullParser.START_DOCUMENT:
startDocument();break;caseXmlPullParser.END_DOCUMENT:
endDocument();
flag= false;//到文档末尾了,结束循环
break;caseXmlPullParser.START_TAG:
startElement(parser);
characters(parser);break;caseXmlPullParser.END_TAG:
endElement(parser);break;default:break;
}
eventCode=parser.next();
}
}catch (XmlPullParserException |IOException e) {
e.printStackTrace();
}
}/*** 开始解析文件时触发的方法*/
public voidstartDocument() {
System.out.println("开始解析这个文件了");
}/*** 结束解析这个xml文件的时候触发*/
public voidendDocument() {
System.out.println("该文件解析完成");
}/*** 开始解析某个标签时触发
* 可以得到jack中【sex=m age=21】部分
*@paramparser*/
public voidstartElement(XmlPullParser parser) {
System.out.println("————开始解析" + currentPrefix +":"+ currentTag + "这个标签了————");for (int i = 0; i < parser.getAttributeCount(); i++) {
String name=parser.getAttributeName(i);
String value=parser.getAttributeValue(i);
System.out.println(name+ " = " +value);
}
}/*** 结束解析某个标签时触发
* 遇到/>时表示一个标签解析完成,而遇到不会触发
*@paramparser*/
public voidendElement(XmlPullParser parser) {
System.out.println("————解析" + currentPrefix +":"+ currentTag + "标签结束————");
}/*** 解析标签中内容时触发
* 得到jack中【jack】的部分
*@paramparser
*@throwsXmlPullParserException
*@throwsIOException*/
public void characters(XmlPullParser parser) throwsXmlPullParserException, IOException {if (currentTag.equals("name")) {
System.out.println("name = " + parser.nextText());//会输出jack
} else if (currentTag.equals("age")) {
System.out.println("age = " + parser.nextText());//会输出21
}
}
}
测试样本:
jack
21
测试结果:
3.XML文件的生成
生成是用简单的pull来做的,没啥技术含量,就是用代码来写xml,最后放到sd卡中
/*** 建立一个xml文件*/
public voidcreatXML() {
XmlSerializer serializer=Xml.newSerializer();
File file= new File(Environment.getExternalStorageDirectory(),"sharpandroid.xml");
FileOutputStream fos= null;try{
fos= newFileOutputStream(file);
serializer.setOutput(fos,"UTF-8");
serializer.startDocument("UTF-8", true);//命名空间+标签名,命名空间可以=null
serializer.startTag(null, "namespace");
serializer.attribute("http://schemas.android.com/apk/res/android", "abc", "egf");
serializer.startTag(null, "persons");for (int i = 0; i < 2; i++) {
serializer.startTag(null, "person");
serializer.attribute(null, "id", i+1+"");
serializer.attribute(null, "age", i+10+"");
serializer.startTag(null, "name");
serializer.text("jack");
serializer.endTag(null, "name");
serializer.endTag(null, "person");
}
serializer.endTag(null, "persons");
serializer.endTag(null, "namespace");
serializer.endDocument();
}catch (IllegalArgumentException | IllegalStateException |IOException e) {//TODO 自动生成的 catch 块
e.printStackTrace();
}finally{try{
fos.flush();
fos.close();
}catch(IOException e) {//TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
测试结果:
全部activity中的代码
packagecom.kale.xml;importjava.io.BufferedReader;importjava.io.ByteArrayInputStream;importjava.io.File;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.InputStreamReader;importjava.io.StringReader;importjavax.xml.parsers.ParserConfigurationException;importjavax.xml.parsers.SAXParserFactory;importorg.xml.sax.InputSource;importorg.xml.sax.SAXException;importorg.xml.sax.XMLReader;importorg.xmlpull.v1.XmlPullParser;importorg.xmlpull.v1.XmlPullParserException;importorg.xmlpull.v1.XmlSerializer;importandroid.app.Activity;importandroid.os.Bundle;importandroid.os.Environment;importandroid.util.Xml;importandroid.view.View;importandroid.widget.Toast;public class MainActivity extendsActivity {
@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
creatXML();
Toast.makeText(this, "xml文件建立成功,在SD卡根目录下sharpandroid.xml", 0).show();
}public voidbuttonListener(View v) {//从assets文件夹中得到test.xml文件的内容
String str = getFromAssets("test.xml");switch(v.getId()) {caseR.id.sax_button://通过sax进行文件的解析
sax(str);break;caseR.id.pull_button://通过pull来解析文件
pull(str);break;default:break;
}
}/*** 通过sax进行解析
*@paramstr*/
public voidsax(String str) {//下面是固定的写法
SAXParserFactory factory =SAXParserFactory.newInstance();
XMLReader reader;try{//得到xmlReader对象
reader =factory.newSAXParser().getXMLReader();//设置内容处理器
reader.setContentHandler(newMyContentHandler());
reader.parse(new InputSource(newStringReader(str)));
}catch (SAXException | ParserConfigurationException |IOException e) {//TODO 自动生成的 catch 块
e.printStackTrace();
}
}/*** 通过pull进行解析
*@paramstr*/
public voidpull(String str) {
XmlPullParser parser=Xml.newPullParser();
InputStream in= newByteArrayInputStream(str.getBytes());try{
parser.setInput(in,"utf-8");
MyXmlPullParserTask task= newMyXmlPullParserTask(parser);
task.execute();//开始解析文件
} catch(XmlPullParserException e) {
e.printStackTrace();
}
}/***@paramfileName
*@returnassets中文件的字符串*/
publicString getFromAssets(String fileName){
String result="";try{
InputStreamReader inputReader= newInputStreamReader( getResources().getAssets().open(fileName) );
BufferedReader bufReader= newBufferedReader(inputReader);
String line="";while((line = bufReader.readLine()) != null) {
result+=line;
}
}catch(Exception e) {
e.printStackTrace();
}returnresult;
}/*** 建立一个xml文件*/
public voidcreatXML() {
XmlSerializer serializer=Xml.newSerializer();
File file= new File(Environment.getExternalStorageDirectory(),"sharpandroid.xml");
FileOutputStream fos= null;try{
fos= newFileOutputStream(file);
serializer.setOutput(fos,"UTF-8");
serializer.startDocument("UTF-8", true);//命名空间+标签名,命名空间可以=null
serializer.startTag(null, "namespace");
serializer.attribute("http://schemas.android.com/apk/res/android", "abc", "egf");
serializer.startTag(null, "persons");for (int i = 0; i < 2; i++) {
serializer.startTag(null, "person");
serializer.attribute(null, "id", i+1+"");
serializer.attribute(null, "age", i+10+"");
serializer.startTag(null, "name");
serializer.text("jack");
serializer.endTag(null, "name");
serializer.endTag(null, "person");
}
serializer.endTag(null, "persons");
serializer.endTag(null, "namespace");
serializer.endDocument();
}catch (IllegalArgumentException | IllegalStateException |IOException e) {//TODO 自动生成的 catch 块
e.printStackTrace();
}finally{try{
fos.flush();
fos.close();
}catch(IOException e) {//TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
布局文件: