Java数据解析之XML
文章大纲
一、xml解析介绍
二、java中xml解析介绍
三、xml解析实战
四、项目源码下载
一、xml解析介绍
最基础的xml解析方式有dom和sax,dom和sax是与平台无关的官方解析方式,是基于事件驱动的解析方式。
1. dom解析图解
dom解析是直接把xml文件放入内存中,如果节点太多的话,就要考虑性能问题了。
2. sax解析图解
sax解析是走到哪个位置,就调用不同方法进行解析。
二、java中xml解析介绍
java中常见解析xml的方式有dom、sax、dom4j、jdom
1. 各大框架比较
dom
优点:
(1)形成树结构,直观好理解,代码更容易编写
(2)解析过程中树结构保留在内存中,方便修改
缺点:
(1)当xml文件较大时,对内存消耗比较大,容易影响解析性能并造成内存溢出
sax
优点:
(1)采用事件驱动模式,对内存消耗较小
(2)适用于只需要处理xml中数据
缺点:
(1)不易编码
(2)很难同时访问同一个xml中的多处不同数据
jdom
优点:
(1)是基于树的处理xml的java api,把树加载到内存中
(2)速度快
缺点:
(1)不能处理大于内存的文档
(2)不支持于dom中相应遍历包
dom4j
优点:
dom4j有更复杂的api,所以dom4j比jdom有更大的灵活性
三、xml解析实战
1. 测试的xml文件:books.xml
<?xml version="1.0" encoding="utf8"?> <bookstore> <book id="1"> <name>冰与火之歌</name> <year>2014</year> <price>89</price> </book> <book id="2"> <name>安徒生童话</name> <year>2004</year> <price>77</price> <language>english</language> </book> </bookstore>
2. 通用实体类book.java
package testall; public class book { private string id; private string name; private string author; private string year; private string price; private string language; 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 getauthor() { return author; } public void setauthor(string author) { this.author = author; } public string getyear() { return year; } public void setyear(string year) { this.year = year; } public string getprice() { return price; } public void setprice(string price) { this.price = price; } public string getlanguage() { return language; } public void setlanguage(string language) { this.language = language; } }
3. dom实战
package dom; import java.io.ioexception; import javax.xml.parsers.documentbuilder; import javax.xml.parsers.documentbuilderfactory; import javax.xml.parsers.parserconfigurationexception; import org.w3c.dom.document; import org.w3c.dom.element; import org.w3c.dom.namednodemap; import org.w3c.dom.node; import org.w3c.dom.nodelist; import org.xml.sax.saxexception; /** * 应用 dom 方式解析 xml * * 在 java 程序中通过自带的 dom 方式解析 xml 文件,解析的内容包括属性名、属性值、节点名以及节点值 * * @author administrator * */ public class domtest { public static void main(string[] args) { //创建一个documentbuilderfactory的对象 documentbuilderfactory dbf = documentbuilderfactory.newinstance(); //创建一个documentbuilder的对象 try { //创建documentbuilder对象 documentbuilder db = dbf.newdocumentbuilder(); //通过documentbuilder对象的parser方法加载books.xml文件到当前项目下 document document = db.parse("books.xml"); //获取所有book节点的集合 nodelist booklist = document.getelementsbytagname("book"); //通过nodelist的getlength()方法可以获取booklist的长度 system.out.println("一共有" + booklist.getlength() + "本书"); //遍历每一个book节点 for (int i = 0; i < booklist.getlength(); i++) { system.out.println("=================下面开始遍历第" + (i + 1) + "本书的内容================="); //通过 item(i)方法 获取一个book节点,nodelist的索引值从0开始 node book = booklist.item(i); // 获取book节点的所有属性集合 namednodemap attrs = book.getattributes(); system.out.println("第 " + (i + 1) + "本书共有" + attrs.getlength() + "个属性"); // 遍历book的属性 for (int j = 0; j < attrs.getlength(); j++) { //通过item(index)方法获取book节点的某一个属性 node attr = attrs.item(j); //获取属性名 system.out.print("属性名:" + attr.getnodename()); //获取属性值 system.out.println("--属性值" + attr.getnodevalue()); } // //前提:已经知道book节点有且只能有1个id属性 // //将book节点进行强制类型转换,转换成element类型 // element book = (element) booklist.item(i); // //通过getattribute("id")方法获取属性值 // string attrvalue = book.getattribute("id"); // system.out.println("id属性的属性值为" + attrvalue); //解析book节点的子节点 nodelist childnodes = book.getchildnodes(); //遍历childnodes获取每个节点的节点名和节点值 system.out.println("第" + (i+1) + "本书共有" + childnodes.getlength() + "个子节点"); for (int k = 0; k < childnodes.getlength(); k++) { //区分出text类型的node以及element类型的node if (childnodes.item(k).getnodetype() == node.element_node) { //获取了element类型节点的节点名 system.out.print("第" + (k + 1) + "个节点的节点名:" + childnodes.item(k).getnodename()); // 获取了element类型节点的节点值 system.out.println("--节点值是:" + childnodes.item(k).getfirstchild().getnodevalue()); // system.out.println("--节点值是:" + childnodes.item(k).gettextcontent()); } } system.out.println("======================结束遍历第" + (i + 1) + "本书的内容================="); } } catch (parserconfigurationexception e) { e.printstacktrace(); } catch (saxexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } }
运行结果如下图所示:
一共有2本书 =================下面开始遍历第1本书的内容================= 第 1本书共有1个属性 属性名:id--属性值1 第1本书共有7个子节点 第2个节点的节点名:name--节点值是:冰与火之歌 第4个节点的节点名:year--节点值是:2014 第6个节点的节点名:price--节点值是:89 ======================结束遍历第1本书的内容================= =================下面开始遍历第2本书的内容================= 第 2本书共有1个属性 属性名:id--属性值2 第2本书共有9个子节点 第2个节点的节点名:name--节点值是:安徒生童话 第4个节点的节点名:year--节点值是:2004 第6个节点的节点名:price--节点值是:77 第8个节点的节点名:language--节点值是:english ======================结束遍历第2本书的内容================= process finished with exit code 0
4. sax实战
解析器saxparserhandler.java如下
package sax; import java.util.arraylist; import org.xml.sax.attributes; import org.xml.sax.saxexception; import org.xml.sax.helpers.defaulthandler; public class saxparserhandler extends defaulthandler { string value = null; book book = null; private arraylist<book> booklist = new arraylist<book>(); public arraylist<book> getbooklist() { return booklist; } int bookindex = 0; /** * xml开始解析时调用 * */ @override public void startdocument() throws saxexception { // todo auto-generated method stub super.startdocument(); system.out.println("sax解析开始"); } /** * xml全部解析完毕时调用 * */ @override public void enddocument() throws saxexception { // todo auto-generated method stub super.enddocument(); system.out.println("sax解析结束"); } /** * 解析元素节点 */ @override public void startelement(string uri, string localname, string qname, attributes attributes) throws saxexception { //调用defaulthandler类的startelement方法 super.startelement(uri, localname, qname, attributes); if (qname.equals("book")) { bookindex++; //创建一个book对象 book = new book(); //开始解析book元素的属性 system.out.println("======================开始遍历某一本书的内容================="); // //已知book元素下属性的名称,根据属性名称获取属性值 // string value = attributes.getvalue("id"); // system.out.println("book的属性值是:" + value); //不知道book元素下属性的名称以及个数,如何获取属性名以及属性值 int num = attributes.getlength(); for(int i = 0; i < num; i++){ system.out.print("book元素的第" + (i + 1) + "个属性名是:" + attributes.getqname(i)); system.out.println("---属性值是:" + attributes.getvalue(i)); if (attributes.getqname(i).equals("id")) { book.setid(attributes.getvalue(i)); } } } else if (!qname.equals("name") && !qname.equals("bookstore")) { system.out.print("节点名是:" + qname + "---"); } } /** * 遇到结束标签时调用 */ @override public void endelement(string uri, string localname, string qname) throws saxexception { //调用defaulthandler类的endelement方法 super.endelement(uri, localname, qname); //判断是否针对一本书已经遍历结束 if (qname.equals("book")) { booklist.add(book); book = null; system.out.println("======================结束遍历某一本书的内容================="); } else if (qname.equals("name")) { book.setname(value); } else if (qname.equals("author")) { book.setauthor(value); } else if (qname.equals("year")) { book.setyear(value); } else if (qname.equals("price")) { book.setprice(value); } else if (qname.equals("language")) { book.setlanguage(value); } } /** * 获取节点的值 */ @override public void characters(char[] ch, int start, int length) throws saxexception { // todo auto-generated method stub super.characters(ch, start, length); value = new string(ch, start, length); if (!value.trim().equals("")) { system.out.println("节点值是:" + value); } } }
解析类saxtest.java
package sax; import java.util.arraylist; import org.xml.sax.attributes; import org.xml.sax.saxexception; import org.xml.sax.helpers.defaulthandler; public class saxparserhandler extends defaulthandler { string value = null; book book = null; private arraylist<book> booklist = new arraylist<book>(); public arraylist<book> getbooklist() { return booklist; } int bookindex = 0; /** * xml开始解析时调用 * */ @override public void startdocument() throws saxexception { // todo auto-generated method stub super.startdocument(); system.out.println("sax解析开始"); } /** * xml全部解析完毕时调用 * */ @override public void enddocument() throws saxexception { // todo auto-generated method stub super.enddocument(); system.out.println("sax解析结束"); } /** * 解析元素节点 */ @override public void startelement(string uri, string localname, string qname, attributes attributes) throws saxexception { //调用defaulthandler类的startelement方法 super.startelement(uri, localname, qname, attributes); if (qname.equals("book")) { bookindex++; //创建一个book对象 book = new book(); //开始解析book元素的属性 system.out.println("======================开始遍历某一本书的内容================="); // //已知book元素下属性的名称,根据属性名称获取属性值 // string value = attributes.getvalue("id"); // system.out.println("book的属性值是:" + value); //不知道book元素下属性的名称以及个数,如何获取属性名以及属性值 int num = attributes.getlength(); for(int i = 0; i < num; i++){ system.out.print("book元素的第" + (i + 1) + "个属性名是:" + attributes.getqname(i)); system.out.println("---属性值是:" + attributes.getvalue(i)); if (attributes.getqname(i).equals("id")) { book.setid(attributes.getvalue(i)); } } } else if (!qname.equals("name") && !qname.equals("bookstore")) { system.out.print("节点名是:" + qname + "---"); } } /** * 遇到结束标签时调用 */ @override public void endelement(string uri, string localname, string qname) throws saxexception { //调用defaulthandler类的endelement方法 super.endelement(uri, localname, qname); //判断是否针对一本书已经遍历结束 if (qname.equals("book")) { booklist.add(book); book = null; system.out.println("======================结束遍历某一本书的内容================="); } else if (qname.equals("name")) { book.setname(value); } else if (qname.equals("author")) { book.setauthor(value); } else if (qname.equals("year")) { book.setyear(value); } else if (qname.equals("price")) { book.setprice(value); } else if (qname.equals("language")) { book.setlanguage(value); } } /** * 获取节点的值 */ @override public void characters(char[] ch, int start, int length) throws saxexception { // todo auto-generated method stub super.characters(ch, start, length); value = new string(ch, start, length); if (!value.trim().equals("")) { system.out.println("节点值是:" + value); } } }
运行结果如下所示:
sax解析开始 ======================开始遍历某一本书的内容================= book元素的第1个属性名是:id---属性值是:1 节点值是:冰与火之歌 节点名是:year---节点值是:2014 节点名是:price---节点值是:89 ======================结束遍历某一本书的内容================= ======================开始遍历某一本书的内容================= book元素的第1个属性名是:id---属性值是:2 节点值是:安徒生童话 节点名是:year---节点值是:2004 节点名是:price---节点值是:77 节点名是:language---节点值是:english ======================结束遍历某一本书的内容================= sax解析结束 ~!~!~!共有2本书 1 冰与火之歌 null 2014 89 null ----finish---- 2 安徒生童话 null 2004 77 english ----finish---- process finished with exit code 0
5. dom4j实战
package dom4j; import java.io.file; import java.util.arraylist; import java.util.iterator; import java.util.list; import org.dom4j.attribute; import org.dom4j.document; import org.dom4j.documentexception; import org.dom4j.element; import org.dom4j.io.saxreader; /** * dom4j 及 jdom 不是 java 提供官方解析 xml 的方式,但都是优秀的解析方法,简单易用;本章介绍在 java 程序中如何通过这两种方式解析 xml 文件 * * @author administrator * */ public class dom4jtest { private static arraylist<book> booklist = new arraylist<book>(); /** * @param args */ public static void main(string[] args) { // 解析books.xml文件 // 创建saxreader的对象reader saxreader reader = new saxreader(); try { // 通过reader对象的read方法加载books.xml文件,获取docuemnt对象。 document document = reader.read(new file("books.xml")); // 通过document对象获取根节点bookstore element bookstore = document.getrootelement(); // 通过element对象的elementiterator方法获取迭代器 iterator it = bookstore.elementiterator(); // 遍历迭代器,获取根节点中的信息(书籍) while (it.hasnext()) { system.out.println("=====开始遍历某一本书====="); element book = (element) it.next(); // 获取book的属性名以及 属性值 list<attribute> bookattrs = book.attributes(); for (attribute attr : bookattrs) { system.out.println("属性名:" + attr.getname() + "--属性值:" + attr.getvalue()); } iterator itt = book.elementiterator(); while (itt.hasnext()) { element bookchild = (element) itt.next(); system.out.println("节点名:" + bookchild.getname() + "--节点值:" + bookchild.getstringvalue()); } system.out.println("=====结束遍历某一本书====="); } } catch (documentexception e) { // todo auto-generated catch block e.printstacktrace(); } } }
运行结果如下所示:
=====开始遍历某一本书===== 属性名:id--属性值:1 节点名:name--节点值:冰与火之歌 节点名:year--节点值:2014 节点名:price--节点值:89 =====结束遍历某一本书===== =====开始遍历某一本书===== 属性名:id--属性值:2 节点名:name--节点值:安徒生童话 节点名:year--节点值:2004 节点名:price--节点值:77 节点名:language--节点值:english =====结束遍历某一本书===== process finished with exit code 0
6. jdom实战
package jdom; import java.io.fileinputstream; import java.io.filenotfoundexception; import java.io.ioexception; import java.io.inputstream; import java.io.inputstreamreader; import java.util.arraylist; import java.util.list; import org.jdom2.attribute; import org.jdom2.document; import org.jdom2.element; import org.jdom2.jdomexception; import org.jdom2.input.saxbuilder; /** * dom4j 及 jdom 不是 java 提供官方解析 xml 的方式,但都是优秀的解析方法,简单易用;本章介绍在 java 程序中如何通过这两种方式解析 xml 文件 * * @author administrator * */ public class jdomtest { private static arraylist<book> bookslist = new arraylist<book>(); /** * @param args */ public static void main(string[] args) { // 进行对books.xml文件的jdom解析 // 准备工作 // 1.创建一个saxbuilder的对象 saxbuilder saxbuilder = new saxbuilder(); inputstream in; try { // 2.创建一个输入流,将xml文件加载到输入流中 in = new fileinputstream("books.xml"); inputstreamreader isr = new inputstreamreader(in, "utf-8"); // 3.通过saxbuilder的build方法,将输入流加载到saxbuilder中 document document = saxbuilder.build(isr); // 4.通过document对象获取xml文件的根节点 element rootelement = document.getrootelement(); // 5.获取根节点下的子节点的list集合 list<element> booklist = rootelement.getchildren(); // 继续进行解析 for (element book : booklist) { book bookentity = new book(); system.out.println("======开始解析第" + (booklist.indexof(book) + 1) + "书======"); // 解析book的属性集合 list<attribute> attrlist = book.getattributes(); // //知道节点下属性名称时,获取节点值 // book.getattributevalue("id"); // 遍历attrlist(针对不清楚book节点下属性的名字及数量) for (attribute attr : attrlist) { // 获取属性名 string attrname = attr.getname(); // 获取属性值 string attrvalue = attr.getvalue(); system.out.println("属性名:" + attrname + "----属性值:" + attrvalue); if (attrname.equals("id")) { bookentity.setid(attrvalue); } } // 对book节点的子节点的节点名以及节点值的遍历 list<element> bookchilds = book.getchildren(); for (element child : bookchilds) { system.out.println("节点名:" + child.getname() + "----节点值:" + child.getvalue()); if (child.getname().equals("name")) { bookentity.setname(child.getvalue()); } else if (child.getname().equals("author")) { bookentity.setauthor(child.getvalue()); } else if (child.getname().equals("year")) { bookentity.setyear(child.getvalue()); } else if (child.getname().equals("price")) { bookentity.setprice(child.getvalue()); } else if (child.getname().equals("language")) { bookentity.setlanguage(child.getvalue()); } } system.out.println("======结束解析第" + (booklist.indexof(book) + 1) + "书======"); bookslist.add(bookentity); bookentity = null; system.out.println(bookslist.size()); system.out.println(bookslist.get(0).getid()); system.out.println(bookslist.get(0).getname()); } } catch (filenotfoundexception e) { e.printstacktrace(); } catch (jdomexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } }
运行结果如下所示:
======开始解析第1书====== 属性名:id----属性值:1 节点名:name----节点值:冰与火之歌 节点名:year----节点值:2014 节点名:price----节点值:89 ======结束解析第1书====== 1 1 冰与火之歌 ======开始解析第2书====== 属性名:id----属性值:2 节点名:name----节点值:安徒生童话 节点名:year----节点值:2004 节点名:price----节点值:77 节点名:language----节点值:english ======结束解析第2书====== 2 1 冰与火之歌 process finished with exit code 0
7. 四种解析方式性能测试
package testall; import java.io.file; import java.io.fileinputstream; import java.io.filenotfoundexception; import java.io.ioexception; import java.io.inputstream; import java.io.inputstreamreader; import java.util.arraylist; import java.util.list; import javax.xml.parsers.documentbuilder; import javax.xml.parsers.documentbuilderfactory; import javax.xml.parsers.parserconfigurationexception; import javax.xml.parsers.saxparser; import javax.xml.parsers.saxparserfactory; import org.dom4j.documentexception; import org.dom4j.io.saxreader; import org.jdom2.attribute; import org.jdom2.jdomexception; import org.jdom2.input.saxbuilder; import org.w3c.dom.document; import org.w3c.dom.namednodemap; import org.w3c.dom.node; import org.w3c.dom.nodelist; import org.xml.sax.saxexception; import dom4j.book; import sax.saxparserhandler; public class parsetest { public static void domxmlparser() { arraylist<book> booklists = new arraylist<book>(); documentbuilderfactory dbf = documentbuilderfactory.newinstance(); try { documentbuilder db = dbf.newdocumentbuilder(); document document = db.parse("books.xml"); nodelist booklist = document.getelementsbytagname("book"); for (int i = 0; i < booklist.getlength(); i++) { node book = booklist.item(i); book bookentity = new book(); namednodemap attrs = book.getattributes(); for (int j = 0; j < attrs.getlength(); j++) { node attr = attrs.item(j); if (attr.getnodename().equals("id")) { bookentity.setid(attr.getnodevalue()); } } nodelist childnodes = book.getchildnodes(); for (int k = 0; k < childnodes.getlength(); k++) { if (childnodes.item(k).getnodetype() == node.element_node) { string name = childnodes.item(k).getnodename(); string value = childnodes.item(k).getfirstchild().getnodevalue(); if (name.equals("name")) { bookentity.setname(value); } else if (name.equals("author")) { bookentity.setauthor(value); } else if (name.equals("year")) { bookentity.setyear(value); } else if (name.equals("price")) { bookentity.setprice(value); } else if (name.equals("language")) { bookentity.setlanguage(value); } } } booklists.add(bookentity); bookentity = null; } } catch (parserconfigurationexception e) { e.printstacktrace(); } catch (saxexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } public static void saxxmlparser(){ saxparserfactory factory = saxparserfactory.newinstance(); try { saxparser parser = factory.newsaxparser(); saxparserhandler handler = new saxparserhandler(); parser.parse("books.xml", handler); } catch (parserconfigurationexception e) { e.printstacktrace(); } catch (saxexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } public static void jdomxmlparser() { arraylist<book> bookslist = new arraylist<book>(); saxbuilder saxbuilder = new saxbuilder(); inputstream in; try { in = new fileinputstream("books.xml"); inputstreamreader isr = new inputstreamreader(in, "utf-8"); org.jdom2.document document = saxbuilder.build(isr); org.jdom2.element rootelement = document.getrootelement(); list<org.jdom2.element> booklist = rootelement.getchildren(); for (org.jdom2.element book : booklist) { book bookentity = new book(); list<attribute> attrlist = book.getattributes(); for (attribute attr : attrlist) { string attrname = attr.getname(); string attrvalue = attr.getvalue(); if (attrname.equals("id")) { bookentity.setid(attrvalue); } } list<org.jdom2.element> bookchilds = book.getchildren(); for (org.jdom2.element child : bookchilds) { if (child.getname().equals("name")) { bookentity.setname(child.getvalue()); } else if (child.getname().equals("author")) { bookentity.setauthor(child.getvalue()); } else if (child.getname().equals("year")) { bookentity.setyear(child.getvalue()); } else if (child.getname().equals("price")) { bookentity.setprice(child.getvalue()); } else if (child.getname().equals("language")) { bookentity.setlanguage(child.getvalue()); } } bookslist.add(bookentity); bookentity = null; } } catch (filenotfoundexception e) { e.printstacktrace(); } catch (jdomexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } public static void dom4jxmlparser(){ arraylist<book> bookslist = new arraylist<book>(); saxreader reader = new saxreader(); try { org.dom4j.document document = reader.read(new file("books.xml")); org.dom4j.element bookstore = document.getrootelement(); list<org.dom4j.element> bookeles = bookstore.elements(); for (org.dom4j.element book : bookeles) { book bookentity = new book(); list<org.dom4j.attribute> bookattrs = book.attributes(); for (org.dom4j.attribute attr : bookattrs) { if (attr.getname().equals("id")) { bookentity.setid(attr.getvalue()); } } list<org.dom4j.element> bookss = book.elements(); for (org.dom4j.element bookchild : bookss) { string name = bookchild.getname(); string value = bookchild.getstringvalue(); if (name.equals("name")) { bookentity.setname(value); } else if (name.equals("author")) { bookentity.setauthor(value); } else if (name.equals("year")) { bookentity.setyear(value); } else if (name.equals("price")) { bookentity.setprice(value); } else if (name.equals("language")) { bookentity.setlanguage(value); } } bookslist.add(bookentity); bookentity = null; } } catch (documentexception e) { e.printstacktrace(); } } public static void main(string[] args) { system.out.println("性能测试:"); //测试dom的性能: long start = system.currenttimemillis(); domxmlparser(); system.out.println("dom:"+ (system.currenttimemillis() - start) ); //测试sax的性能: start = system.currenttimemillis(); saxxmlparser(); system.out.println("sax:"+ (system.currenttimemillis() - start) ); //测试jdom的性能: start = system.currenttimemillis(); jdomxmlparser(); system.out.println("jdom:"+ (system.currenttimemillis() - start) ); //测试dom4j的性能: start = system.currenttimemillis(); dom4jxmlparser(); system.out.println("dom4j:"+ (system.currenttimemillis() - start) ); } }
运行结果如下所示:
性能测试: dom:78 sax解析开始 ======================开始遍历某一本书的内容================= book元素的第1个属性名是:id---属性值是:1 节点值是:冰与火之歌 节点名是:year---节点值是:2014 节点名是:price---节点值是:89 ======================结束遍历某一本书的内容================= ======================开始遍历某一本书的内容================= book元素的第1个属性名是:id---属性值是:2 节点值是:安徒生童话 节点名是:year---节点值是:2004 节点名是:price---节点值是:77 节点名是:language---节点值是:english ======================结束遍历某一本书的内容================= sax解析结束 sax:13 jdom:88 dom4j:82 process finished with exit code 0
总结:
jdom 在性能测试时表现不佳,在测试 10m 文档时内存溢出。在小文档情况下还值得考虑使用 jdom。dom4j不适合大文件的解析,因为它是一下子将文件加载到内存中,所以有可能出现内存溢出,sax是基于事件来对xml进行解析的,所以他可以解析大文件的xml,也正是因为如此,所以dom4j可以对xml进行灵活的增删改查和导航,而sax没有这么强的灵活性,所以sax经常是用来解析大型xml文件,而要对xml文件进行一些灵活(crud)操作就用dom4j。
四、项目源码下载
上一篇: 常见的判断网站PR劫持的办法
下一篇: django中的auth详解