利用DOM、SAX技术解析XML文档
我们知道XML文档现在大多用来传输数据和做配置文件,但不管是什么样的应用,都离不开读取XML中保存的数据信息。下面我们就来看看在java中,通过sun公司发布的API如何来解析获取XML中的数据信息。
解析XML,有2种技术,一种是DOM,一种是SAX。
DOM全称:Document Object Model(文档对象模型),这种技术会将整个XML文档全部加载到内存中,形成一棵倒立的文档树。我们通过字面意思可以这样来理解:文档对象模型(将整个XML文档,加载到内存,按照预先的模型,形成一棵目录树对象,我们对数据节点(一会再说什么是节点)的所有操作,都在这棵树上)。正因为XML的完整信息都加载到了内存,所以,DOM可以对内存中的这棵文档树修改/添加/删除节点,并能写入到XML文档中(对XML文档进行实际的操作),还可以任意来回滚动访问任意节点。
什么是节点呢?在文档树中,一切都是节点,例:
<stu>
<oneStu id = "10">
<name>ltz</name>
<age>22</age>
</oneStu>
</stu>
这个XML在文档树中的节点数有11个,怎么说呢?<stu></stu>是一个,<stu>与<oneStu>之间的空格也是一个,同样的,<oneStu>是一个,<oneStu>与<name>之间也是一个,<name></name>是一个,ltz又是一个。。。这就是一切都是节点。。。(当然了,我们这里没有加入层次的概念,只是单纯的说明在DOM中,一切都是节点)。
还有个问题需要注意的是,在XML的根元素加载到文档树中以后,会在根元素的上面再有一个节点。也就是XML中的跟元素,到DOM中就不是根节点了。
下面我们来看看如何利用DOM技术获取XML中的信息:
package net.ltz.dom;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class ForSummary{
/**
* 获取Document对象
* @return 返回Document对象
* @throws Exception
*/
private Document getDOC(String xmlPath) throws Exception{
//建造文档建造工厂对象
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//生产出文档建造对象
DocumentBuilder db = dbf.newDocumentBuilder();
//建造解析xml文件的文档对象
Document doc = db.parse(xmlPath);
return doc;
}
/**
* 获取文档树的信息(这里就不使用递归了,主要是熟悉DOM本身的语法操作)
* @param doc
*/
private void getMSG(Document doc){
//因为进来以后,得到的是xml的根元素的父节点,前面叙述的时候有讲到
//所以要找到根元素就要获取当前节点的子节点
Node fristElement = doc.getFirstChild();
//获取节点名
String fristElementName = fristElement.getNodeName();
//获取节点类型1对应的是元素,2是属性,3是文档,共有12种,可以参看API
int fristElementType = fristElement.getNodeType();
//获取节点的值,只有元素、文本节点有值,其他的节点调用这个方法返回NULL
String firstElementValue = fristElement.getNodeValue();
System.out.println("节点名:"+fristElementName+" 节点类型:"+fristElementType+" 节点值:"+firstElementValue);
fristElement.getTextContent();//获取从这个节点往下的所有的文本节点内容。返回String
//获取同级节点
//因为我现在还在xml的根元素这,所以,要继续往下才有同级节点
Node secondNode = fristElement.getLastChild();//getFrist/lastChild是获取当前节点的第一/最后一个子节点
secondNode.getNextSibling();//获取同级下一个
secondNode.getPreviousSibling();//获取同级上一个
//获取指定标签的节点
NodeList defineNodeList = doc.getElementsByTagName("db");
//因为在一个xml中,元素名相同的很多,所以返回的是一个NodeList
for(int i = 0;i<defineNodeList.getLength();i++){
//注意,NodeList不是用[]来指定下标,是用item()
System.out.println(defineNodeList.item(i).getNodeName());
}
//获取父级节点
Node parentNode = secondNode.getParentNode();
}
public static void main(String [] args) throws Exception{
ForSummary fs = new ForSummary();
Document doc = fs.getDOC("db.xml");
fs.getMSG(doc);
}
}
至于修改和保存XML,请百度一下^_^,太多了。。。很难写
SAX技术:基于事件驱动的,在操作的时候,不会将XML完整的加载到内存。而是从上到下的依次到xml中去读取,并且在每个元素的开始、结束处都会触发一个事件。如果我们要处理的话,就捕捉这个事件,不处理不用管就行。这样的方式有个缺点就是不能来回滚动,不管读哪里的数据,都要从头到尾依次读,也不能往xml中写入数据。但是它不占用多大的内存空间。
private void getXMLUseSAX()throws Exception{
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
sp.parse("db.xml", new DefaultHandler(){
@Override
public void startDocument() throws SAXException {
System.out.println("开始读取文档");
}
@Override
public void endDocument() throws SAXException {
System.out.println("结束读取文档");
}
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
System.out.println("读取元素 "+qName+" 开始");
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
System.out.println("读取元素 "+qName+" 结束");
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
System.out.println("文档内容:"+new String(ch,start,length));
}
});
}
这两种技术都有优缺点,所以,我们一般会结合使用。在要操作xml或者对接点进行修改的时候,就用DOM,只读取的话,可以用SAX.