一、Pull解析
案例一:Pull解析(读取)及写入XML文件
一、Pull解析
1. Pull解析:
XML Pull解析器是一款高效、易用的XML解析器,足以与DOM和SAX媲美。
SAX采用的是一种“推”的解析方式(通过解析事件来推动解析动作),而XML PULL采用的是“拉”的方式,从XML流(文件流或字符流)中拉出标记内容。
2. XML Pull API的解析方式融合了SAX和DOM方式,主要体现在以下3点:
(1)XML Pull API和DOM方式一样,都无需单独制定解析事件处理器,调用模块可以直接获取解析结果。
(2)XML Pull API也有解析事件的概念,但其中解析事件不是不是用于推送解析动作,而是用于分析当前获取内容的类型。
(3)XML Pull API也是采用与SAX解析相同的“猴子掰玉米”的方式,按照文件顺序进行扫描分析,无需对标记内容进行存储和管理。
XML Pull API在易用性方面要比SAX方式有所提高;在执行效率方面又要比DOM方式有优势。
3. XML Pull API使用过程:
(1)获取XML Pull解析工厂(XmlPullParserFactory)实例。
(2)借助工厂实例创建一个XML Pull解析器(XmlPullParser)。
(3)设置XML Pull解析器的输入内容。
(4)通过XML Pull解析器的有关方法进行解析。
(5)解析得到的结果可以直接提供给可视界面进行显示。
案例一:Pull解析(读取)及写入XML文件
1. 在java下面创建 persons.xml。里面写入XML内容。
<?xml version="1.0" encoding="UTF-8"?> <persons> <person id="10"> <name>段誉</name> <age>16</age> </person> <person id="11"> <name>乔峰</name> <age>32</age> </person> </persons> <!-- 1. Table: persons : 根元素 person子节点:一条记录 属性id:主键列 name/age:其他字段 name/age的文本节点:其他字段的值 Element Node 元素节点 Text Node 文本节点 xm.substring(start,length) new String(ch,start,length) -->
2. 创建 com.android.pojo 包,里面创建 Person.java 的 pojo 类,用来封装XML文件中的一条记录。
package com.android.pojo; /** * Created by Xiangdong Lee on 2015/8/25. */ public class Person { private String id; private String name; private String age; @Override public String toString() { return "Person{" + "id=" +id +",name='" +name + '\'' +",age="+age+'?'; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
3. 创建com.android.service包,用于放处理业务逻辑的类。在该包下创建 PullPersonService.java 的类,是一个使用DOM方式解析XML的业务逻辑类。
package com.android.service; import android.util.Xml; import com.android.pojo.Person; import org.xmlpull.v1.XmlPullParser; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /** * Pull 解析 xml: * 只拿 xml 中有用的东西。又快又容易理解。 * <p/> * XML Pull API使用过程: * 1、获取XML Pull解析工厂(XmlPullParserFactory)实例。 * 2、借助工厂实例创建一个XML Pull解析器(XmlPullParser)。 * 3、设置XML Pull解析器的输入内容。 * 4、通过XML Pull解析器的有关方法进行解析。 * 5、解析得到的结果可以直接提供给可视界面进行显示 * <p/> * Created by Xiangdong Lee on 2015/8/26. */ public class PullPersonService { public static List<Person> getPersons(InputStream is) throws Exception { List<Person> persons = null; Person person = null; XmlPullParser parser = Xml.newPullParser(); parser.setInput(is, "utf-8"); // 产生第一个事件 int eventType = parser.getEventType(); // 当文档结束事件时退出循环 while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { // 开始文档 case XmlPullParser.START_DOCUMENT: // new 集合,方便于添加元素 persons = new ArrayList<>(); break; // 开始标记 case XmlPullParser.START_TAG: // 获得当前节点名(标记名) String name = parser.getName(); if ("person".equals(name)) { person = new Person(); // 获得当前节点名的第一个属性(id)的值 person.setId(parser.getAttributeValue(0)); } if (person != null) { if ("name".equals(name)) { // 获取当前节点名的文本节点的值 person.setName(parser.nextText()); } if ("age".equals(name)) { person.setAge(parser.nextText()); } } break; // 结束标记 case XmlPullParser.END_TAG: // 当结束标记为 person时 if ("person".equals(parser.getName())) { persons.add(person); // 清空。方便于加载第二个 person = null; } break; } // 获得解析器中的下一个事件 eventType = parser.next(); } return persons; } }
4. 在测试包下面创建一个单元测试类:PersonServiceTest,用于进行JUnit单元测试。
package com.android.dataparsing; import android.test.InstrumentationTestCase; import android.util.Log; /** * Junit 单元测试 -- 要先连接真机/模拟器。 * 第一步.继承 InstrumentationTestCase 测试案例类 * 第二步.定义测试方法:方法名必须以 小写的 testXxx 开头 * 第三步.选中方法名,右击 -> Run -> testXxx() * * Created by Xiangdong on 2015/8/25. */ public class PersonServiceTest extends InstrumentationTestCase { private static final String TAG = "MainActivity"; public void testLog() { Log.v(TAG, "This is Junit."); } }
5. 在测试类中对Pull解析进行测试。写testPull测试方法。
package com.android.dataparsing; import android.test.InstrumentationTestCase; import android.util.Log; import com.android.pojo.Person; import com.android.service.PullPersonService; import java.io.InputStream; import java.util.List; /** * Junit 单元测试 -- 要先连接真机/模拟器。 * 第一步.继承 InstrumentationTestCase 测试案例类 * 第二步.定义测试方法:方法名必须以 小写的 testXxx 开头 * 第三步.选中方法名,右击 -> Run -> testXxx() * <p/> * Created by Xiangdong on 2015/8/25. */ public class PersonServiceTest extends InstrumentationTestCase { private static final String TAG = "MainActivity"; public void testLog() { Log.v(TAG, "This is Junit."); } /** * Android 中解析 xml 方式 :使用 Pull 解析 xml(重点) * * @throws Exception */ public void testPull() throws Exception { InputStream is = getClass().getClassLoader().getResourceAsStream("persons.xml"); List<Person> persons = PullPersonService.getPersons(is); for (Person person : persons) { Log.v(TAG, person.toString()); } } }
运行的时候,会报错:
因为不能直接读取到java文件夹下面的persons.xml文件,而是通过PullPersonService类中,从流里面读取的。流中的文件是从真机/虚拟机中获取的。
而persons.xml文件并不在真机/虚拟机中,因此要打包,将其放到其中。
6. 使用流读取 classpath 下的文件(通过手工命令方式):
然后,再测试,就可以正常解析了。
6. 在PullPersonService中,编写 写入XML 的方法:save()。
/** * 写入 xml 文件 * * @param persons * @param write * @throws Exception */ public static void save(List<Person> persons, Writer write) throws Exception { XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(write); serializer.startDocument("utf-8", true); // namespace 命名空间, String name 根元素(根标记) serializer.startTag(null, "persons"); /* 迭代集合的数据 */ for (Person person : persons) { serializer.startTag(null, "person"); serializer.attribute(null, "id", person.getId()); serializer.startTag(null, "name"); serializer.text(person.getName()); serializer.endTag(null, "name"); serializer.startTag(null, "age"); serializer.text(person.getAge()); serializer.endTag(null, "age"); serializer.endTag(null, "person"); } serializer.endTag(null, "persons"); serializer.endDocument(); // 刷新(把缓存里的刷新进去) write.flush(); write.close(); }
7. PersonServiceTest。测试 写入XML 的方法:testPullSave()。
/** * Android中 写入 xml 文件。 * @throws Exception */ public void testPullSave() throws Exception { InputStream is = getClass().getClassLoader().getResourceAsStream("persons.xml"); List<Person> persons = PullPersonService.getPersons(is); // 生成到内存中 --- (也可以写入文件中) StringWriter writer = new StringWriter(); PullPersonService.save(persons,writer); Log.v(TAG,writer.toString()); // 写入文件中 // 功能清单文件 - 赋权限 // 在 SD卡根目录下面创建 xml 文件 File dir = Environment.getExternalStorageDirectory(); File file = new File(dir,"persons.xml"); if(!file.exists()){ file.createNewFile(); } // false:不追加,覆盖。 FileWriter out = new FileWriter(file,false); out.write(writer.toString()); out.close(); }
AndroidManifest.xml。要写入文件中,需要在功能清单中赋予权限:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.dataparsing" > <!-- 功能清单文件 - 赋权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
这样,就可以把文件写入内存,或者写入文件中了(可以在手机/虚拟机中看到这个文件)。
代码补充:
1. PullPersonService
package com.android.service; import android.util.Xml; import com.android.pojo.Person; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import java.io.InputStream; import java.io.Writer; import java.util.ArrayList; import java.util.List; /** * Pull 解析 xml: * 只拿 xml 中有用的东西。又快又容易理解。 * <p/> * XML Pull API使用过程: * 1、获取XML Pull解析工厂(XmlPullParserFactory)实例。 * 2、借助工厂实例创建一个XML Pull解析器(XmlPullParser)。 * 3、设置XML Pull解析器的输入内容。 * 4、通过XML Pull解析器的有关方法进行解析。 * 5、解析得到的结果可以直接提供给可视界面进行显示 * <p/> * Created by Xiangdong Lee on 2015/8/26. */ public class PullPersonService { public static List<Person> getPersons(InputStream is) throws Exception { List<Person> persons = null; Person person = null; XmlPullParser parser = Xml.newPullParser(); parser.setInput(is, "utf-8"); // 产生第一个事件 int eventType = parser.getEventType(); // 当文档结束事件时退出循环 while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { // 开始文档 case XmlPullParser.START_DOCUMENT: // new 集合,方便于添加元素 persons = new ArrayList<>(); break; // 开始标记 case XmlPullParser.START_TAG: // 获得当前节点名(标记名) String name = parser.getName(); if ("person".equals(name)) { person = new Person(); // 获得当前节点名的第一个属性(id)的值 person.setId(parser.getAttributeValue(0)); } if (person != null) { if ("name".equals(name)) { // 获取当前节点名的文本节点的值 person.setName(parser.nextText()); } if ("age".equals(name)) { person.setAge(parser.nextText()); } } break; // 结束标记 case XmlPullParser.END_TAG: // 当结束标记为 person时 if ("person".equals(parser.getName())) { persons.add(person); // 清空。方便于加载第二个 person = null; } break; } // 获得解析器中的下一个事件 eventType = parser.next(); } return persons; } /** * 写入 xml 文件 * * @param persons * @param write * @throws Exception */ public static void save(List<Person> persons, Writer write) throws Exception { XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(write); serializer.startDocument("utf-8", true); // namespace 命名空间, String name 根元素(根标记) serializer.startTag(null, "persons"); /* 迭代集合的数据 */ for (Person person : persons) { serializer.startTag(null, "person"); serializer.attribute(null, "id", person.getId()); serializer.startTag(null, "name"); serializer.text(person.getName()); serializer.endTag(null, "name"); serializer.startTag(null, "age"); serializer.text(person.getAge()); serializer.endTag(null, "age"); serializer.endTag(null, "person"); } serializer.endTag(null, "persons"); serializer.endDocument(); // 刷新(把缓存里的刷新进去) write.flush(); write.close(); } }
2. PersonServiceTest
package com.android.dataparsing; import android.os.Environment; import android.test.InstrumentationTestCase; import android.util.Log; import com.android.pojo.Person; import com.android.service.PullPersonService; import java.io.File; import java.io.FileWriter; import java.io.InputStream; import java.io.StringWriter; import java.util.List; /** * Junit 单元测试 -- 要先连接真机/模拟器。 * 第一步.继承 InstrumentationTestCase 测试案例类 * 第二步.定义测试方法:方法名必须以 小写的 testXxx 开头 * 第三步.选中方法名,右击 -> Run -> testXxx() * <p/> * Created by Xiangdong on 2015/8/25. */ public class PersonServiceTest extends InstrumentationTestCase { private static final String TAG = "MainActivity"; public void testLog() { Log.v(TAG, "This is Junit."); } /** * Android 中解析 xml 方式 :使用 Pull 解析 xml(重点) * * @throws Exception */ public void testPull() throws Exception { InputStream is = getClass().getClassLoader().getResourceAsStream("persons.xml"); List<Person> persons = PullPersonService.getPersons(is); for (Person person : persons) { Log.v(TAG, person.toString()); } } /** * Android中 写入 xml 文件。 * @throws Exception */ public void testPullSave() throws Exception { InputStream is = getClass().getClassLoader().getResourceAsStream("persons.xml"); List<Person> persons = PullPersonService.getPersons(is); // 生成到内存中 --- (也可以写入文件中) StringWriter writer = new StringWriter(); PullPersonService.save(persons,writer); Log.v(TAG,writer.toString()); // 写入文件中 // 功能清单文件 - 赋权限 // 在 SD卡根目录下面创建 xml 文件 File dir = Environment.getExternalStorageDirectory(); File file = new File(dir,"persons.xml"); if(!file.exists()){ file.createNewFile(); } // false:不追加,覆盖。 FileWriter out = new FileWriter(file,false); out.write(writer.toString()); out.close(); } }