XML解析

来源:互联网 发布:过度锻炼的危害知乎 编辑:程序博客网 时间:2024/05/19 00:39

一、简介

在得到一个XML文档之后,利用程序按照其中元素的定义名称取出对应的内容,这样的操作即为:XML解析。在XML解析中,W3C定义了SAX和DOM两种解析方式,者两种解析方式的程序操作如下图所示:


由上图即可发现,应用程序不是直接对XMl文档进行操作的,而是先由XML分析器对文档进行分析,然后应用程序通过XML分析器所提供的DOM接口或SAX接口对分析结果进行操作,从而间接地实现了对XML文档的访问。

二、DOM解析操作

基于DOM(Document Object Model, 文档对象模型)的XML解析器将一个XML文档转换成一个对象模型的集合(即:DOM树),应用程序通过对这个对象模型的操作,来实现对XNL文档数据的操作。通过DOM接口,应用程序可以在任何时候访问XML文档中的任何一部分数据,因此这种利用DOM接口的机制也被称为随机访问机制。DOM树所提供的随机访问方式给应用程序的开发带来了很大的灵活性,可以任意的控制整个XML文档中的内容。但是,由于DOM分析器把整个XML文档转化成DOM树放在内存中,因此,此文档比较大或者结构比较复杂时,堆内存的要求就比较高,而且对于结构复杂的树的遍历也是一项耗时的操作。所以,DOM分析器对及其性能的要求比较高,程序的效率并不十分理想。

package com.wbf.dom;import java.io.File;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Document;import org.w3c.dom.NodeList;public class TestDOMDemo {public static void main(String args[]){//建立DocumentBuilderFactory,用于取得DocumentBuilderDocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//建立DocumentBuilderDocumentBuilder builder = null;//实例化DocumentDocument doc = null;try {builder = factory.newDocumentBuilder();//读取指定路径的xml文档doc = builder.parse("d:" + File.separator + "dom_demo_1.xml");//查找tagName="name"的节点NodeList nl = doc.getElementsByTagName("name");int len = nl.getLength();for (int i = 0; i < len; i++){//输出文本节点的内容System.out.println(nl.item(i).getFirstChild().getNodeValue());}} catch (Exception e) {e.printStackTrace();}}}

运行结果:

                   张三
                   李四

XML源文件dom_demo_1.xml内容:

<?xml version="1.0" encoding="GBK"?><addresslist><linkman><name>张三</name><email>zhangsan@qq.com</email></linkman><linkman><name>李四</name><email>lisi@qq.com</email></linkman></addresslist>
以上程序完成了一个简单的DOM解析操作,从指定的XML文件中读取指定节点的内容,当使用builder.parse()操作时实际上就相当于将所有的XML文档读取到内存中,从而将所有的XML文件内容按照节点的定义顺序将其变成一颗内存中的DOM树,供用户解析使用。
在DOM操作中,除了可以使用DOM完成XML的读取之外,还可以使用DOM进行XML文件的新建输出。此时就需要必须newDocument方法建立一个新的DOOM树,并手工设置各个节点的关系。
package com.wbf.dom;import java.io.File;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.transform.OutputKeys;import javax.xml.transform.Transformer;import javax.xml.transform.TransformerFactory;import javax.xml.transform.dom.DOMSource;import javax.xml.transform.stream.StreamResult;import org.w3c.dom.Document;import org.w3c.dom.Element;public class TestDOMDemo1 {public static void main(String args[]) throws Exception{//建立DocumentBuilderFactory,用于取得DocumentBuilderDocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//建立DocumentBuilderDocumentBuilder builder = factory.newDocumentBuilder();//新建一个Document文档Document doc = builder.newDocument();//建立各个操作节点Element addresslist = doc.createElement("addresslist");Element linkman = doc.createElement("linkman");Element name = doc.createElement("name");Element email = doc.createElement("email");//为需要节点添加文本节点name.appendChild(doc.createTextNode("张三"));email.appendChild(doc.createTextNode("zhangsan@qq.com"));//设置节点关系linkman.appendChild(name);linkman.appendChild(email);addresslist.appendChild(linkman);doc.appendChild(addresslist);//输出文档到文件中TransformerFactory tf = TransformerFactory.newInstance();Transformer t = tf.newTransformer();t.setOutputProperty(OutputKeys.ENCODING, "GBk");//设置编码格式DOMSource source = new DOMSource(doc);//输出文档//设置输出位置StreamResult result = new StreamResult(new File("d:" + File.separator + "dom_demo_2.xml"));//输出t.transform(source, result);}}
运行结果文件dom_demo_2.xml:
<?xml version="1.0" encoding="GBk" standalone="no"?><addresslist>    <linkman>        <name>张三</name>        <email>zhangsan@qq.com</email>    </linkman></addresslist>

以上程序可以发现,在XML创建中的所有节点(ELement)都是通过Document接口创建的,在创建时本身并没有任何父子节点关系,而是通过appendChild()方法设置的,而且根节点(addresslist)也要加入到document中,这样才是一棵完整的DOM。从以上两个demo中可以发现,使用DOM操作不仅可以读取文件,本身也可以生成和修改XML文件。

三、SAX解析操作

SAX(Simple APIS for XML, 操作XML的简单接口),采用的是一种顺序的模式进行访问,是一种快速读取XML数据的方式。如果在开发中想要使用SAX解析,则首先应该编写一个SAX解析器,再直接定义一个类,并使该类继承自DefaultHandler类,同时覆写其方法即可。当使用SAX解析器进行操作时会触发一系列的事件,如:当扫描到文档开始与结束、元素开始与结束是都会调用相关的处理方法,并由这些操作方法做出相应的操作,直至整个文档扫描结束。

新建解析器,继承DefaultHandler类:

package com.wbf.sax;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.DefaultHandler;public class MySAX extends DefaultHandler {@Overridepublic void characters(char[] ch, int start, int length)throws SAXException {/* 接收元素中字符数据的通知 * 默认情况下,不执行任何操作。 * 应用程序编写者可以重写此方法,以便对每块字符数据采取特定的措施(如将该数据添加到节点或缓冲区,或者将该数据打印到文件)。 * */System.out.println(new String(ch, start, length));}@Overridepublic void endDocument() throws SAXException {/* 接收文档结束的通知。  * 默认情况下,不执行任何操作。应用程序编写者可以在子类中重写此方法,以便在文档的结束处采取特定的操作(如,结束树或关闭输出文件)。 * */System.out.println("\n文档读取结束。。。");}@Overridepublic void endElement(String uri, String localName, String qName)throws SAXException {/* 接收元素结束的通知。  * 默认情况下,不执行任何操作。应用程序编写者可以在子类中重写此方法,以便在每个元素的结束处采取特定的操作(如,结束树节点或将输出写入文件)。 * */System.out.print("</");System.out.print(qName);//输出元素名称System.out.print(">");}@Overridepublic void startDocument() throws SAXException {/* 接收文档开始的通知。 * 默认情况下,不执行任何操作。应用程序编写者可以在子类中重写此方法,以便在文档的开始采取特定的措施(如分配树的根节点或创建输出文件)。 * */System.out.println("<?xml version=\"1.0\" encoding=\"GBK\"?>");}@Overridepublic void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {/* 接收元素开始的通知。  * 默认情况下,不执行任何操作。应用程序编写者可以在子类中重写此方法,以便在每个元素的开始处采取特定的操作(如,分配新的树节点或将输出写入文件)。 * */System.out.print("<");System.out.print(qName);if (attributes != null){for (int i = 0; i < attributes.getLength(); i++){System.out.print(" " + attributes.getQName(i) + "=\"" + attributes.getValue(i) + "\"");}}System.out.println(">");}}
使用SAX解析器:
package com.wbf.sax;import java.io.File;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;public class TestSAXDemo {public static void main(String[] args) throws Exception {//建立SAX解析工厂SAXParserFactory factory = SAXParserFactory.newInstance();//构造解析器SAXParser parser = factory.newSAXParser();//使用DefaultHandler解析XMLparser.parse("d:" + File.separator + "sax_demo_1.xml", new MySAX());}}
被解析XML文档sax_demo_1.xml:
<?xml version="1.0" encoding="GBK"?><addresslist><linkman><name>张三</name><email>zhangsan@qq.com</email></linkman><linkman><name>李四</name><email>lisi@qq.com</email></linkman></addresslist>

四、DOM解析和SAX解析比较


既然DOM解析适合于修改,SAX解析适合于读取大文件,那么如果将两者的优点集合起来操作岂不是更加方便?如:JDOM、DOM4J等。

五、JDOM

JDOM是使用Java语言编写的,用于读、写、操作XML的一套组件。一定要记住:JDOM = DOM修改文件的优点 + SAX读取快速的优点 。

1)使用JDOM进行XML的写操作(新建)

package com.wbf.jdom;import java.io.File;import java.io.FileOutputStream;import org.jdom2.Attribute;import org.jdom2.Document;import org.jdom2.Element;import org.jdom2.output.XMLOutputter;public class WriteXML {public static void main(String[] args) throws Exception {Element addresslist = new Element("addresslist");//定义根节点Element linkman = new Element("linkman");//定义linkman节点Element name = new Element("name");//定义name节点Element email = new Element("email");//定义email节点Attribute id = new Attribute("id", "zs");//定义属性Document doc = new Document(addresslist);//定义一个document对象,并设置根节点//设置节点的文本节点name.setText("zhangsan");email.setText("zhangsan@qq.com");//设置节点的属性linkman.setAttribute(id);//设置节点的父子关系linkman.addContent(name);linkman.addContent(email);addresslist.addContent(linkman);XMLOutputter out = new XMLOutputter();//用来输出XML文件out.setFormat(out.getFormat().setEncoding("GBK"));//设置输出的编码格式//输出XML文件out.output(doc, new FileOutputStream("d:" + File.separator + "jdom_demo_1.xml"));}}
被JDOM新建的XML文档jdom_demo_1.xml:
<?xml version="1.0" encoding="GBK"?><addresslist>    <linkman id="zs">        <name>zhangsan</name>        <email>zhangsan@qq.com</email>    </linkman></addresslist>
2)使用JDOM进行XML的读操作
package com.wbf.jdom;import java.io.File;import java.util.List;import org.jdom2.Document;import org.jdom2.Element;import org.jdom2.input.SAXBuilder;public class ReadXML {public static void main(String[] args) throws Exception {//使用JDOM读取XML文件SAXBuilder builder = new SAXBuilder();//建立SAX解析//被解析的XML文档Document read_doc = builder.build("d:" + File.separator + "jdom_demo_1.xml");Element root = read_doc.getRootElement();List<Element> elements = root.getChildren("linkman");for (int i = 0; i < elements.size(); i++){Element e = elements.get(i);//取出一个linkman子元素String name = e.getChildText("name");//取得name元素内容或文本节点的valueString id = e.getAttribute("id").getValue();//获取linkman节点的属性id//String email = e.getChild("email").getText();//取得email元素内容//String email = e.getChild("email").getValue();//取得email元素内容String email = e.getChildText("email");//与前两句等价System.out.println("------联系人-----");System.out.println("姓名: " + name + " 编号: " + id);System.out.println("EMAIL:" + email);System.out.println("----------------");System.out.println();}}}
其中jdom_demo_1.xml即为前文中所示。

六、DOM4J

DOM4J也是一组XML操作的组件包,主要用来读写XML文件。由于DOM4J性能优异,功能强大,而且易用性强,所以应用广泛,如:Hibernate和Spring框架中都使用了DOM4J进行XML的解析操作。其操作与JDOM类似。

1)使用DOM4进行XML的操作

package com.wbf.dom4j;import java.io.File;import java.io.FileOutputStream;import org.dom4j.Document;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.dom4j.io.OutputFormat;import org.dom4j.io.XMLWriter;public class Dom4JWriter {public static void main(String[] args) throws Exception {Document doc = DocumentHelper.createDocument();//新建文档Element addresslist = doc.addElement("addresslist");//定义根节点Element linkman = addresslist.addElement("linkman");//定义子节点Element name = linkman.addElement("name");//定义子节点Element email = linkman.addElement("email");//定义子节点linkman.addAttribute("id", "zs");name.setText("张三");//设置name节点内容email.setText("zhangsan@qq.com");//设置email节点内容OutputFormat format = OutputFormat.createPrettyPrint();//设置输出格式format.setEncoding("GBK");//指定输出目标文件和格式XMLWriter writer = new XMLWriter(new FileOutputStream(new File("d:" + File.separator + "dom4j_demo_1.xml")), format);//输出文件内容writer.write(doc);//关闭输出流writer.close();}}

输出的XML文档如下:

<?xml version="1.0" encoding="GBK"?><addresslist>  <linkman id="zs">    <name>张三</name>    <email>zhangsan@qq.com</email>  </linkman></addresslist>
2)使用DOM4进行XML的操作
package com.wbf.dom4j;import java.io.File;import java.util.Iterator;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;public class DOM4JReader {public static void main(String[] args) throws Exception{//新建即将被读取的文件File file = new File("d:" + File.separator + "dom4j_demo_1.xml");SAXReader reader = new SAXReader();//建立SAX解析Document doc = reader.read(file);//读取文档Element root = doc.getRootElement();//获取根节点Iterator iter = root.elementIterator();while(iter.hasNext()){Element linkman = (Element)iter.next();System.out.println("姓名: " + linkman.elementText("name") + ", 编号: " + linkman.attributeValue("id"));System.out.println("邮件: " + linkman.elementText("email"));}}}
程序运行结果:

                       姓名: 张三, 编号: zs
                       邮件: zhangsan@qq.com

七、总结

从以上程序中可以看出,读取文件需要使用SAX建立解析器,然后通过文档依次找到根节点,再通过根节点找到每一个节点的内容。