java拾遗3----XML解析(三) StAX PULL解析
使用PULL方式解析XML:
Pull是STAX的一个实现
StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API
StAX通过提供一种基于事件迭代器(Iterator)的API让程序员去控制xml文档解析过程。
为什么说StAX方式的效率优于SAX呢?
因为SAX 是推模式的,所有的操作在解析器自动控制下进行,所有事件都会处理,不管需不需要解析整个文档,解析器都会自动启动解析任务,然后按顺序向下解析,直到解析完成才终止。而StAX 是拉模式的,可以由用户根据需要控制需要处理事件类型及何时终止解析任务,用户调用一次next(),解析器就进行一次向下解析,完全在用户的控制下进行解析操作,在需要的解析工作已经完成时,可以随时终止解析。
简单说来,推模式,就是你把如何操作通通告诉解析器,之后,解析器自动去完成所有解析任务,你不能做任何干涉了。
而拉模式就像抽纸,随用随拉,需要时就拉一下,不需要时,它不会自动推出来。你可以一直拉把一盒抽纸拉完,也可以按自己需要只拉几张。
可以从网上下载PULL解析的第三方jar包,来使用PULL解析XML
www.xmlpull.org上有3种PULL的实现可以下载:
由描述信息可以看出,XPP3这种实现主要致力于性能和易于使用,而且,android内置的PULL解析器似乎也是XPP3,所以可以下载这种实现,来实现XML的PULL解析。
选择合适的XPP3下载:
如,我下载了:
下载完成后,解压缩,然后找到文件名类似于xpp3-1.1.4c.jar的jar包,这里面就有我们需要的API。
doc目录中的api目录中存放着API帮助手册,可以参考该手册,学习使用XPP3 PULL解析器。
如同DOM和SAX解析XML一样,PULL解析XML也要用到解析器工厂类XmlPullParserFactory,解析器类XmlPullParser,XPP3中还提供了XmlSerializer类用于将内存对象实现序列化到xml文档。
工厂类也是通过静态方法newInstance()获得工厂类对象。
newPullParser()和newSerializer()方法分别用于获取XmlPullParser对象和XmlSerializer对象。
以上就是工厂类的常用的三个方法。
XmlPulParser通过setInput()方法,设置要解析的XML文档。
通过调用next方法,一步步向下解析
通过调用getEventType()方法,来确认当前解析XML所触发的事件类型。
事件类型通过XmlPullParser中定义的几个常量来标识:
START_DOCUMENT:开始解析触发
START_DOCUMENT:碰到节点时触发
TEXT:读取字符文本时触发
END_TAG:碰到结束节点时触发
END_DOCUMENT:解析完成时触发
当触发事件类型是START_TAG或者END_TAG时,可以通过getName()方法获取当前节点名称。
当事件类型是START_TAG时,可以通过nextText()获取该节点的文本子节点内容,若事件类型为END_TAG则会返回一个空字符串。
当事件类型是START_TAG时,通过getAttributeCount()方法,可以获取该节点的属性个数
通过getAttributeName(int index)方法,可以获取指定index的属性的名称
通过getAttributeValue(int index)方法,可以获取指定index属性的值。
以上,是XmlPullParser类中的几个常用方法。
XmlSerializer实现将内存中的数据序列化到XML文档中,也有几个常用的方法:
setOutput()方法,指定要将数据序列化到哪个XML文件中
startDocument()方法,指定XML文档的开始<?xml声明信息
如:startDocument(“utf-8”, true);将在XML文档中输出如下信息:
<?xml encoding="UTF-8" standalone="yes"?>
startTag()开始一个节点,namespace没有,一般设置为null或者空字符串
text()设置文本内容
endTage()结束一个节点。
attribute()设置节点的属性。
了解了这几个类的常用方法后,就可以实现PULL模式的XML解析和序列化了。但是,现在有个问题:DOM可以实现增删改查操作,PULL模式可以吗?
由于在DOM模式下,整个文档对象都在内存中,所以可以实现修改回写。但是PULL模式以及SAX模式下,都是边解析边释放内存的,不能直接实现增删改查操作。但是,可以模仿DOM模式,把解析的部分保存在内存中,如把解析的对象都保存到一个List中。然后所有的增删改查操作,都针对List对象进行,所有操作执行完成之后,再把内存中最终版本的List序列化到Xml文档中,就间接实现了PULL模式下的增删改查操作。
具体操作可以参考下面代码:
待解析的students.xml:
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 3 <students> 4 5 <student id="000"> 6 7 <name>zhangsan22</name> 8 9 <gender>female</gender> 10 11 <age>26</age> 12 13 </student> 14 15 <student id="002"> 16 17 <name>lisi</name> 18 19 <gender>male</gender> 20 21 <age>24</age> 22 23 </student> 24 25 <student id="003"> 26 27 <name>xiaoqiao</name> 28 29 <gender>female</gender> 30 31 <age>18</age> 32 33 </student> 34 35 <student id="004"> 36 37 <name>diaochan</name> 38 39 <gender>female</gender> 40 41 <age>23</age> 42 43 </student> 44 45 </students>
对应的实体类:Student.java:
1 package cn.csc.bean; 2 3 public class Student { 4 5 private String id = null; 6 7 private String name = null; 8 9 private String gender = null; 10 11 private int age = 0; 12 13 public String getId() { 14 15 return id; 16 17 } 18 19 public void setId(String id) { 20 21 this.id = id; 22 23 } 24 25 public String getName() { 26 27 return name; 28 29 } 30 31 public void setName(String name) { 32 33 this.name = name; 34 35 } 36 37 public String getGender() { 38 39 return gender; 40 41 } 42 43 public void setGender(String gender) { 44 45 this.gender = gender; 46 47 } 48 49 public int getAge() { 50 51 return age; 52 53 } 54 55 public void setAge(int age) { 56 57 this.age = age; 58 59 } 60 61 public Student(String id, String name, String gender, int age) { 62 63 super(); 64 65 this.id = id; 66 67 this.name = name; 68 69 this.gender = gender; 70 71 this.age = age; 72 73 } 74 75 public Student() { 76 77 super(); 78 79 } 80 81 public String toString() { 82 83 return "[id:"+id+",name:"+name+",gender:"+gender+",age"+age+"]"; 84 85 } 86 87 }
StudentPullUtils类,实现两个方法,一个用于将xml文件中的student解析到List<Student>中,一个用于将List<Student>中的数据序列化到指定的xml文件中:
1 public class StudentPullUtils { 2 3 public static List<Student> parseXml(String filename) throws Exception{ 4 5 List<Student> list = new ArrayList<Student>(); 6 7 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 8 9 XmlPullParser parser = factory.newPullParser(); 10 11 parser.setInput(new FileInputStream(filename), "utf-8"); 12 13 int eventType; 14 15 Student s = null; 16 17 while((eventType = parser.getEventType())!= XmlPullParser.END_DOCUMENT){ 18 19 if(eventType == XmlPullParser.START_TAG && parser.getName().equals("student")){ 20 21 s = new Student(); 22 23 s.setId(parser.getAttributeValue(0)); 24 25 } 26 27 if(eventType == XmlPullParser.START_TAG && parser.getName().equals("name")){ 28 29 s.setName(parser.nextText()); 30 31 } 32 33 if(eventType == XmlPullParser.START_TAG && parser.getName().equals("age")){ 34 35 s.setAge(Integer.parseInt(parser.nextText())); 36 37 } 38 39 if(eventType == XmlPullParser.START_TAG && parser.getName().equals("gender")){ 40 41 s.setGender(parser.nextText()); 42 43 } 44 45 if(eventType == XmlPullParser.END_TAG && parser.getName().equals("student")){ 46 47 list.add(s); 48 49 } 50 51 parser.next(); 52 53 } 54 55 return list; 56 57 } 58 59 60 61 public static boolean serializeList(List<Student> list, String filename) throws Exception{ 62 63 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 64 65 XmlSerializer serializer = factory.newSerializer(); 66 67 serializer.setOutput(new FileOutputStream(filename), "utf-8"); 68 69 serializer.startDocument("utf-8", true); 70 71 serializer.startTag(null, "students"); 72 73 for(Student s : list){ 74 75 serializer.startTag(null, "student"); 76 77 serializer.attribute(null, "id", s.getId()); 78 79 serializer.startTag(null, "name"); 80 81 serializer.text(s.getName()); 82 83 serializer.endTag(null, "name"); 84 85 serializer.startTag(null, "gender"); 86 87 serializer.text(s.getGender()); 88 89 serializer.endTag(null, "gender"); 90 91 serializer.startTag(null, "age"); 92 93 serializer.text(s.getAge()+""); 94 95 serializer.endTag(null, "age"); 96 97 serializer.endTag(null, "student"); 98 99 } 100 101 serializer.endTag(null, "students"); 102 103 serializer.endDocument(); 104 105 106 107 return true; 108 109 } 110 111 }
调用测试:实现将数据从students.xml中读取到Lis<Student>中,所有学生的年龄加1,然后序列化到students_bak.xml中:
1 List<Student> list = StudentPullUtils.parseXml("students.xml"); 2 3 for(Student s: list){ 4 5 s.setAge(s.getAge()+1); 6 7 } 8 9 StudentPullUtils.serializeList(list, "students_bak.xml");