用SAX和PULL进行XML文件的解析与生成
XML解析有传统的dom方法还有Jsoup,SAX,PULL等,这里讲的是比较省内存的SAX和PULL方法。Android中极力推荐用PULL的方式来解析,我个人觉得pull确实比较简单,但其内部的逻辑性不是很分明。所以今天做了个类来将其中的多个步骤进行了分割,以后直接拿来用即可。
1.SAX:
首先先讲解SAX中各个方法的作用:
我们以这个不规则的xml语句做例子:
<abc:kale sex=m age=21>jack</abc:kale>
startDocument:开始解析一个xml文件时触发
endDocument:这个xml文件被解析完毕时触发
startElement:开始解析xml文件中的一个标签时触发,这里可以得到标签名和其中的各个属性值。
如:从<person age = 12 sex = f/>会得到标签名:【person】和属性值:【age = 12 sex = f】
endElement:结束解析一个标签时触发
characters:解析这个标签内部的内容时触发,这里可以得到这个标签子节点中的内容。
如:从<name>jack<name>中得到【jack】
下面是实现代码:
1.首先建立一个SAX对象,然后进行解析工作。这里会要自己建立一个ContentHandler的子类
/** * 通过sax进行解析 * @param str */ public void sax(String str) { //下面是固定的写法 SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader reader; try { //得到xmlReader对象 reader = factory.newSAXParser().getXMLReader(); //设置内容处理器 reader.setContentHandler(new MyContentHandler()); reader.parse(new InputSource(new StringReader(str))); } catch (SAXException | ParserConfigurationException | IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } }
MyContentHandler.java 这个类就是来处理事务的,里面有各种回调方法
package com.kale.xml; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * @author:Jack Tony * @tips :举个极端的例子:<input type="hidden" name="UserType">kale</input> * startElement中可以得到的是:type="hidden" name="UserType" * characters中得到的是:kale * @date :2014-10-11 */ public class MyContentHandler extends DefaultHandler{ //当前正在解析的标签名 private String currentTag; /* * 开始解析这个xml文件的时候触发 */ @Override public void startDocument() throws SAXException { System.out.println("开始解析这个文件了"); } /* * 结束解析这个xml文件的时候触发 */ @Override public void endDocument() throws SAXException { System.out.println("文件解析结束"); } /* * 开始解析每个元素的时候触发 * <person age = 12 sex = f/> * <kale:name>jack<kale:name> * 1.uri:当前正在解析的元素的命名空间 * 2.localName:不带前缀的这个元素的名字——>name * 3.qName:带前缀的这个元素命——>kale:name * 4.attributes:得到的元素中的属性——>age=12 set=f */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //举例:<input type="hidden" name="UserType" id="UserType" value="1"> 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) * 停止解析这个元素的时候触发 */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { // TODO 自动生成的方法存根 super.endElement(uri, localName, qName); System.out.println("————-解析"+qName+"标签结束————"); } /* * 得到元素中的内容,比如下面的jack * <name>jack<name> */ @Override public void characters(char[] ch, int start, int length) throws SAXException { //举例:<name>jack<name> 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文件可能是不规范的,所以处理时要考虑异常):
<?xml version="1.0" encoding="utf-8"?> <namespace xmlns:abc="http://schemas.android.com/apk/res/android"> <form id="form1" name="form1" method="post" action="ok" style="margin: 0; padding: 0;" > <input type="hidden" name="UserType" id="12" value="1"/> <abc:name>jack</abc:name> <abc:age>21</abc:age> </form> </namespace>
测试结果:
2.PULL
其实PULL中就一个重要的方法XmlPullParser.next();,正因如此才让其变得简单很多。PULL的特点是运行到什么状态是没有回调方法的,它进行某个处理状态时,会改变一个状态变量,通过getEventType()就可以来判断当前是处于什么状态了。
推荐浏览:http://384444165.iteye.com/blog/1521332
但正因为内部逻辑需要开发者来处理,所以变得结构不是很清晰。我这里通过一个类来将其转换为SAX的框架,以后只需要复写这些方法便可以直接进行操作了。至于运行到哪一步,看方法名酒明白了。同样还是之前的那幅图:
这里面的方法的作用也是和SAX一样的。下面是使用的代码:
1.建立这个类的对象,执行操作
/** * 通过pull进行解析 * @param str */ public void pull(String str) { XmlPullParser parser = Xml.newPullParser(); InputStream in = new ByteArrayInputStream(str.getBytes()); try { parser.setInput(in,"utf-8"); MyXmlPullParserTask task = new MyXmlPullParserTask(parser); task.execute();//开始解析文件 } catch (XmlPullParserException e) { e.printStackTrace(); } }
2.进行解析处理
MyXmlPullParserTask.java
我通过前面的switch-case语句将处理的状态进行了分割,这些状态可以完全类比到SAX中。这样便于理解!
package com.kale.xml; import java.io.IOException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; public class MyXmlPullParserTask { /** * 当前正在解析的标签名,类似于sax中的localName * 可以得到<abc:kale sex=m age=21>jack</abc:kale>中【kale】 */ private String currentTag; /** * 当前正在解析的标签名的前缀,sax中的qName=前缀+当前标签名 * 可以得到<abc:kale sex=m age=21>jack</abc:kale>中【abc】 */ private String currentPrefix; /** * 当前正在解析的标签的命名空间,类似于sax中的uri * 得到 * <namespace xmlns:abc="http://schemas.android.com/apk/res/android"> * <abc:kale sex=m age=21>jack</abc:kale> * 中的【http://schemas.android.com/apk/res/android】 */ private String currentNamespace; private XmlPullParser parser; public MyXmlPullParserTask(XmlPullParser parser) { this.parser = parser; } /** * 开始解析的方法,这里已经写好了,尽量不要该这里的代码。 */ public void execute() { try { // 得到当前状态的标识代码 int eventCode = parser.getEventType(); // 如果当前状态不是文档结束,那么就继续循环 boolean flag = true; while (flag) { // 当前解析元素的标签,不带前缀 currentTag = parser.getName(); currentNamespace = parser.getNamespace(); currentPrefix = parser.getPrefix(); switch (eventCode) { case XmlPullParser.START_DOCUMENT: startDocument(); break; case XmlPullParser.END_DOCUMENT: endDocument(); flag = false;// 到文档末尾了,结束循环 break; case XmlPullParser.START_TAG: startElement(parser); characters(parser); break; case XmlPullParser.END_TAG: endElement(parser); break; default: break; } eventCode = parser.next(); } } catch (XmlPullParserException | IOException e) { e.printStackTrace(); } } /** * 开始解析文件时触发的方法 */ public void startDocument() { System.out.println("开始解析这个文件了"); } /** * 结束解析这个xml文件的时候触发 */ public void endDocument() { System.out.println("该文件解析完成"); } /** * 开始解析某个标签时触发 * 可以得到<abc:kale sex=m age=21>jack</abc:kale>中【sex=m age=21】部分 * @param parser */ public void startElement(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); } } /** * 结束解析某个标签时触发 * 遇到/>时表示一个标签解析完成,而遇到</xxx>不会触发 * @param parser */ public void endElement(XmlPullParser parser) { System.out.println("————解析" + currentPrefix +":"+ currentTag + "标签结束————"); } /** * 解析标签中内容时触发 * 得到<name>jack</name>中【jack】的部分 * @param parser * @throws XmlPullParserException * @throws IOException */ public void characters(XmlPullParser parser) throws XmlPullParserException, IOException { if (currentTag.equals("name")) { System.out.println("name = " + parser.nextText());// 会输出jack } else if (currentTag.equals("age")) { System.out.println("age = " + parser.nextText());// 会输出21 } } }
测试样本:
<?xml version="1.0" encoding="utf-8"?> <namespace xmlns:abc="http://schemas.android.com/apk/res/android"> <form id="form1" name="form1" method="post" action="ok" style="margin: 0; padding: 0;" > <input type="hidden" name="UserType" id="12" value="1"/> <abc:name>jack</abc:name> <abc:age>21</abc:age> </form> </namespace>
测试结果:
3.XML文件的生成
生成是用简单的pull来做的,没啥技术含量,就是用代码来写xml,最后放到sd卡中
/** * 建立一个xml文件 */ public void creatXML() { XmlSerializer serializer = Xml.newSerializer(); File file = new File(Environment.getExternalStorageDirectory(),"sharpandroid.xml"); FileOutputStream fos = null; try { fos = new FileOutputStream(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中的代码
package com.kale.xml; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.util.Xml; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); creatXML(); Toast.makeText(this, "xml文件建立成功,在SD卡根目录下sharpandroid.xml", 0).show(); } public void buttonListener(View v) { //从assets文件夹中得到test.xml文件的内容 String str = getFromAssets("test.xml"); switch (v.getId()) { case R.id.sax_button: //通过sax进行文件的解析 sax(str); break; case R.id.pull_button: //通过pull来解析文件 pull(str); break; default: break; } } /** * 通过sax进行解析 * @param str */ public void sax(String str) { //下面是固定的写法 SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader reader; try { //得到xmlReader对象 reader = factory.newSAXParser().getXMLReader(); //设置内容处理器 reader.setContentHandler(new MyContentHandler()); reader.parse(new InputSource(new StringReader(str))); } catch (SAXException | ParserConfigurationException | IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } /** * 通过pull进行解析 * @param str */ public void pull(String str) { XmlPullParser parser = Xml.newPullParser(); InputStream in = new ByteArrayInputStream(str.getBytes()); try { parser.setInput(in,"utf-8"); MyXmlPullParserTask task = new MyXmlPullParserTask(parser); task.execute();//开始解析文件 } catch (XmlPullParserException e) { e.printStackTrace(); } } /** * @param fileName * @return assets中文件的字符串 */ public String getFromAssets(String fileName){ String result=""; try { InputStreamReader inputReader = new InputStreamReader( getResources().getAssets().open(fileName) ); BufferedReader bufReader = new BufferedReader(inputReader); String line=""; while((line = bufReader.readLine()) != null) { result += line; } } catch (Exception e) { e.printStackTrace(); } return result; } /** * 建立一个xml文件 */ public void creatXML() { XmlSerializer serializer = Xml.newSerializer(); File file = new File(Environment.getExternalStorageDirectory(),"sharpandroid.xml"); FileOutputStream fos = null; try { fos = new FileOutputStream(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(); } } } }
布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="${relativePackage}.${activityClass}" > <Button android:id="@+id/sax_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="通过SAX来解析" android:onClick="buttonListener"/> <Button android:id="@+id/pull_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:text="通过PULL来解析" android:onClick="buttonListener"/> </RelativeLayout>
源码下载:http://download.csdn.net/detail/shark0017/8028375