XML文档最基本的解析方式有两种,文档对象模型(DOM)和XML解析的简单API(SAX)
1,DOM解析
核心:
DOM解析是一种基于对象的API,它把XML的内容加载到内存中,生成一个与XML文档内容相对应的对象模型,这样根据树的结构,以结点形式来对文档进行操作。
过程:
使用DOM解析首先要一个DocumentBuilder对象,它可以通过pare(File file)来解析一个XML文件,并返回该XML文件对应的Document对象。DocumentBuilder是一个抽象类,因而不能直接实例化,可以使用DocumentBuilderFactory构建一个DocumentBuilder对象。去的Document对象之后,就可以使用Document的各种方法来遍历整个XML内容了。
即:先用DocumentBuilderFactory构建一个DocumentBuilder对象,然后由DocumentBuilder通过parse得到一个Document对象,这样就可以使用了!!!
DocumentBuilderFactory:
构造方法摘要 | |
---|---|
protected |
DocumentBuilderFactory() 用于阻止实例化的受保护构造方法。 |
构造方法详细信息 |
---|
用于阻止实例化的受保护构造方法。请使用 newInstance()
。
static DocumentBuilderFactory |
newInstance() 获取 DocumentBuilderFactory 的新实例。 |
abstract DocumentBuilder |
newDocumentBuilder() 使用当前配置的参数创建一个新的 DocumentBuilder 实例。 |
DocumentBuilder:
Document |
parse(File f) 将给定文件的内容解析为一个 XML 文档,并且返回一个新的 DOM Document 对象。 |
Document |
parse(String uri) 将给定 URI 的内容解析为一个 XML 文档,并且返回一个新的 DOM Document 对象。 |
Document |
parse(InputStream is) 将给定 InputStream 的内容解析为一个 XML 文档,并且返回一个新的 DOM Document 对象。 |
abstract Document |
newDocument() 获取 DOM Document 对象的一个新实例来生成一个 DOM 树。 |
abstract DOMImplementation |
getDOMImplementation() 获取 DOMImplementation 对象的一个实例。 |
Schema |
getSchema() 获取由 XML 处理器使用的 Schema 的引用。 |
Document:
DocumentType |
getDoctype() 与此文档相关的文档类型声明(参见 DocumentType )。 |
Element |
getDocumentElement() 这是一种便捷属性,该属性允许直接访问文档的文档元素的子节点。 |
Element |
getElementById(String elementId) 返回具有带给定值的 ID 属性的 Element 。 |
NodeList |
getElementsByTagName(String tagname) 按文档顺序返回包含在文档中且具有给定标记名称的所有 Element 的 NodeList 。 |
Attr |
createAttributeNS(String namespaceURI, String qualifiedName) 创建给定的限定名称和名称空间 URI 的属性。 |
CDATASection |
createCDATASection(String data) 创建其值为指定字符串的 CDATASection 节点。 |
Element |
createElement(String tagName) 创建指定类型的元素。 |
Text |
createTextNode(String data) 创建给定指定字符串的 Text 节点。 |
下面我们来看一个示例程序:先初始化一个XML文件,然后使用DOM对象来解析这个XML文件,初始化XML文件之后会把这个文件路径打印出来,可以找到这个文件,看一下XML文件的结果,然后从运行结果看DOM解析的结果。
package com.book.ch15.xml; import java.io.File; import java.io.FileOutputStream; 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.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /*涉及到的类:DocumentBuilderFactory,DocumentBuilder,Document, * Element,NodeList,Node,都是在xml解析中常用到的类*/ public class DOMTest { //初始化一个XML文件 public static void initFile(File xmlFile) throws IOException { StringBuffer buffer = new StringBuffer(); buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append("\r\n"); buffer.append("<!DOCTYPE notes>").append("\r\n"); buffer.append("<notes>").append("\r\n"); buffer.append(" <note hasRead=\"true\" ID=\"1\">").append("\r\n"); buffer.append(" <from>Ordm</from>").append("\r\n"); buffer.append(" <to>Lin</to>").append("\r\n"); buffer.append(" <head>Reminder</head>").append("\r\n"); buffer.append(" <body>Don't forget me this weekend!</body>").append("\r\n"); buffer.append(" <date>2007-4-1 12:00:00</date>").append("\r\n"); buffer.append(" </note>").append("\r\n"); buffer.append("\r\n"); buffer.append(" <note hasRead=\"false\" ID=\"2\">").append("\r\n"); buffer.append(" <from>Lin</from>").append("\r\n"); buffer.append(" <to>Ordm</to>").append("\r\n"); buffer.append(" <head>Re: Reminder</head>").append("\r\n"); buffer.append(" <body>Ok. I will call you then. </body>").append("\r\n"); buffer.append(" <date>2007-4-1 12:31:25</date>").append("\r\n"); buffer.append(" </note>").append("\r\n"); buffer.append("</notes>").append("\r\n"); FileOutputStream ous = null; try{ ous = new FileOutputStream(xmlFile); ous.write(buffer.toString().getBytes("UTF-8")); ous.flush(); }catch(IOException e){ e.printStackTrace(); }finally{ if(ous != null){ ous.close(); } } } //main方法,解析上述文件 public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException{ File file = new File("dom_test.xml"); initFile(file);//生成要解析的初始化xml文件 System.out.println("File: " + file.getAbsolutePath()); // 声明一个 DocumentBuilder. 抽象类,不能直接构建,可以通过 DocumentFactory 来构建。 DocumentBuilder builder = null; // 声明一个 DocumentBuilderFactory. 这是一个单态模式 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); // 取得默认的 DocumentBuilder. builder = builderFactory.newDocumentBuilder(); Document document = builder.parse(file);//注意一个Document的生成过程 Element root = document.getDocumentElement();//Element类!!! System.out.println("根元素:" + root.getNodeName()); NodeList childNodes = root.getChildNodes();//第一层root的子节点,NodeList类!!! for(int i=0; i<childNodes.getLength(); i++){ Node node = childNodes.item(i);//Node类!!! if("note".equals(node.getNodeName())){ System.out.print("\r\n找到一条 Note 记录. ID: " + node.getAttributes().getNamedItem("ID").getNodeValue() + ". "); if("true".equalsIgnoreCase(node.getAttributes().getNamedItem("hasRead").getNodeValue())){ System.out.println("\t已读"); } else{ System.out.println("\t新邮件"); } NodeList nodeDetail = node.getChildNodes();//继续遍历子节点 for(int j=0; j<nodeDetail.getLength(); j++){ Node detail = nodeDetail.item(j); if("from".equals(detail.getNodeName())){ System.out.println("发信人: " + detail.getTextContent()); } else if("to".equals(detail.getNodeName())){ System.out.println("收信人: " + detail.getTextContent()); } else if("head".equals(detail.getNodeName())){ System.out.println("主题: " + detail.getTextContent()); } else if("body".equals(detail.getNodeName())){ System.out.println("内容: " + detail.getTextContent()); } else if("date".equals(detail.getNodeName())){ System.out.println("日期: " + detail.getTextContent()); } } } } } }
初始化的xml文件:
2,SAX解析
SAX与DOM是两种功能差不多但是底层实现不太一样的解析方式。DOM在解析的时候会把整个xml文件全部映射为Document里的树状结构,换而言之,xml的每个元素,属性,文本,注释都在Document里有所体现,对于小的XML文件来说,这样处理还是比较方便的。但是遇到比较大的文件的时候,DOM的缺点就看出来了,它占用的内存比较大。而且查找速度也比较慢。
SAX就是针对这种情况出现的解决方案,SAX解析器对XML文档进行解析时,会从xml文档的开始位置进行解析,同时根据已经定义好的事件处理器,来决定当前解析的部分(元素,属性,或是元素内容)是否有必要记录并存储。
SAX解析使用SAXPaser,这也是一个抽象类,不能实例化,可以通过SAXParserFactory来构建一个实例,然后调用SAXParser的parse(File f,DefaultHander dh)来解析xml文档,第一个参数是要解析的xml文件,第二个参数是决定要处理什么样的元素和属性。该函数不返回Document对象,所有的处理过程都是在第二个参数里完成的。
DefaultHander 是一个默认的Hander,包含几个解析xml时触发的方法,不过默认是什么都不做,它需要继承然后覆盖它的方法,添加所需要的操作代码。
DefaultHander常用方法:
主要是要理解上面四种方法的含义和执行方式:
startDocument() 在文档开始处采取的操作
endDocument()在文档结束处采取的操作
startElement()在每个元素的开始处采取的操作
endElement()在每个元素结束处采取的操作
void |
characters(char[] ch, int start, int length) 接收元素中字符数据的通知。 |
void |
endDocument() 接收文档结束的通知。 |
void |
endElement(String uri, String localName, String qName) 接收元素结束的通知。 |
void |
startDocument() 接收文档开始的通知。 |
void |
startElement(String uri, String localName, String qName, Attributes attributes) 接收元素开始的通知。 |
package com.book.ch15.xml; import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class SAXTest { public static void initFile(File xmlFile) throws IOException{ DOMTest.initFile(xmlFile);//调用上一个程序的方法初始化要解析的xml文件 } public static void main(String[] args) throws IOException, ParserConfigurationException, SAXException{ File file = new File("sax_test.xml"); initFile(file); System.out.println("File: " + file.getAbsolutePath()); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser();//注意一个parser的得到过程 parser.parse(file, new MyHandler());//所有解析的工作都放在DefaultHandler的子类MyHandler中了 } } class MyHandler extends DefaultHandler { static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private String content; public static void log(Object obj){ //System.out.println(dateFormat.format(new java.util.Date()) + " [" + MyHandler.class.getName() + "] " + obj); } @Override public void characters(char[] ch, int start, int length) throws SAXException { content = new String(ch, start, length); if(content.length() > 0){ //log("content: " + content); } } @Override public void endDocument() throws SAXException { log("endDocument(); "); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { //log("endElement(\"" + uri +"\", \"" + localName + "\", \"" + qName + "\"); \r\n"); if("from".equals(qName)){ System.out.println("发信人: " + content); } else if("to".equals(qName)){ System.out.println("收信人: " + content); } else if("head".equals(qName)){ System.out.println("标题: " + content); } else if("body".equals(qName)){ System.out.println("内容: " + content); } else if("date".equals(qName)){ System.out.println("日期: " + content); } } @Override public void startDocument() throws SAXException { //log("startDocument(); "); } //@Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //log("startElement(\"" + uri + "\", \"" + localName + "\", \"" + qName + "\", " + attributes + "); "); if("note".equals(qName)){ System.out.println("\r\n找到一条 note 记录:, ID: " + attributes.getValue("ID") + ". \t" + ("true".equals(attributes.getValue("hasRead")) ? "已读" : "新邮件" )); } } }
结果:
File: E:\eclipse\workspace\JAVAbase\sax_test.xml
找到一条 note 记录:, ID: 1. 已读
发信人: Ordm
收信人: Lin
标题: Reminder
内容: Don't forget me this weekend!
日期: 2007-4-1 12:00:00
找到一条 note 记录:, ID: 2. 新邮件
发信人: Lin
收信人: Ordm
标题: Re: Reminder
内容: Ok. I will call you then.
日期: 2007-4-1 12:31:25
3,使用JDOM简化xml解析
JDOM的xml解析主要使用SAXBuilder,SAXBuilder类的parse(File file)可以解析一个xml文件并返回一个JDOM的Document对象,这有点像前面的DOM解析,JDOM也有DOMBuilder类,不过这个DOMBuilder不解析xml文档,只负责把org.w3c.dom.Document和org.w3c.dom.Element对象转org.jdom.Document和org.jdom.Element。
JDom解析一个xml文档比较简单,只需要Document document = new SAXBuilder(“jdom.xml”);这句话就把xml文档封装成了Document对象,剩下的就只需要通过Document来取得自己需要的元素或者属性了。
package com.book.ch15.xml; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.Iterator; import java.util.List; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; public class JDOMTest { //初始化一个xml,构建xml文件的形式是以document对象的形式,跟前面字符串缓存的形式大不一样 public static void initXML(File xmlFile) throws IOException { Document document = new Document(); Element rootElement = new Element("notes"); { Element note = new Element("note"); note.setAttribute("hasRead", "true"); note.setAttribute("ID", "1"); note.addContent(new Element("from").setText("Ordm")); note.addContent(new Element("to").setText("Lin")); note.addContent(new Element("head").setText("Reminder")); note.addContent(new Element("body").setText("Don't forget me this weekend")); note.addContent(new Element("date").setText("2007-4-1 12:00:00")); rootElement.addContent(note); } { Element note = new Element("note"); note.setAttribute("hasRead", "false"); note.setAttribute("ID", "2"); note.addContent(new Element("from").setText("Lin")); note.addContent(new Element("to").setText("Ordm")); note.addContent(new Element("head").setText("Re: Reminder")); note.addContent(new Element("body").setText("Ok. I will call you then. ")); note.addContent(new Element("date").setText("2007-4-1 12:31:25")); rootElement.addContent(note); } document.setRootElement(rootElement); XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); outputter.output(document, new FileOutputStream(xmlFile));//将这个document对象输出为xml文件 } public static void main(String[] args) throws JDOMException, IOException{ File file = new File("jdom_test.xml"); initXML(file); System.out.println(file.getAbsolutePath()); //构建一个JDom的Document对象 SAXBuilder builder = new SAXBuilder(); Document document = builder.build(file); Element rootElement = document.getRootElement(); List notes = rootElement.getChildren("note");//notes类型 for(Iterator it=notes.iterator(); it.hasNext(); ){ Element note = (Element)it.next(); System.out.println("\r\n找到一个 note 消息, ID: " + note.getAttributeValue("ID") + ". \t" + ("true".equals(note.getAttributeValue("hasRead")) ? "已读" : "新消息")); List details = note.getChildren(); for(Iterator iterator=details.iterator(); iterator.hasNext(); ){ Element detail = (Element)iterator.next(); if("from".equals(detail.getName())){ System.out.println("发信人: " + detail.getTextTrim()); } else if("to".equals(detail.getName())){ System.out.println("收信人: " + detail.getTextTrim()); } else if("head".equals(detail.getName())){ System.out.println("标题: " + detail.getTextTrim()); } else if("body".equals(detail.getName())){ System.out.println("内容: " + detail.getTextTrim()); } else if("date".equals(detail.getName())){ System.out.println("日期: " + detail.getTextTrim()); } } } } }