一. XML简介
1.概念
2.XML文档结构
3. DTD和schema
4. 显示XML
5. XSL
6. XPATH
二. XML处理模型
1.XML文档处理模型
2.SAX和DOM比较
3.小结
三. SAX
四. DOM
五. Dom4J
1. 概念
XML(Extensible Markup Language,可扩展标记语言)是一个用于构造其他语言的元语言。XML描述了创造这些语言的规则,每种语言都互不相同,但都使用标签来标记内容。XML定义的语言的一个例子是XHTML,相当于是XML的词汇。目前,XML已经成为一种通用的数据交换格式,是一种用于描述结构化信息的技术,它具有平台无关性,语言无关性,系统无关性,为数据交换带来了极大的便利。
2. XML文档结构
1.1 XML文档结构图
3. DTD和schema
DTD和schema包含了用于解释文档是如何构成的规则,通过DTD或schema可以定义XML词汇。这些规则定义了每个元素的子元素和属性。可以根据DTD或schema来验证一个文档是否是合法的。由于DTD和schema内容比较多,这里不做详细介绍。
(1) DTD Document Type Defination
DTD描述的是文档的结构,它指明一个元素可以出现多少次、是否可选以及它是否包含属性等,这样就定义了一个文档的类型。可以直接将DTD写入一个XML文件,或DTD为一个独立的文件,然后在XML文件中通过文档声明对其引用。
(2) schema
schema也用于定义XML文档的结构,但它的功能更强,结构也更复杂。schema也是XML词汇的一种,完全符合XML的规则。
4. 显示XML
可以通过CSS或XSL将XML的内容可视化地展现出来,这样就可以显示在Web浏览器或者打印出来。CSS可以按照应用于XHTML的方式应用于XML,但是XML中引入CSS的方法只有一种:就是通过处理指令将样式表关联到XML文档中,
<?xml-stylesheet type="text/css" href="style.css"?>
CSS有一个缺点就是它是按照元素在XML文档中出现的顺序来呈现他们,不能够对它们进行排序,过滤。CSS大家比较熟悉,这里不做深入探讨。下面将介绍一下XSL,另一种功能更强大的显示XML的技术。
5. XSL
XSL(Extensible Sheet Language, 可扩展样式表语言)分为两 部分:XSLT(XSL转换)和XSL-FO(XSL格式对象)。XSLT将XML源文档转换为另一个XML文档(结果树)。XSL-FO为结果树添加格式。因为XHTML是XML的一个词汇(XML定义的语言),所以XSLT可以将XML转换为XHTML,并XHTML添加样式表,然后显示在Web浏览器中,从而实现XML的显示。
下面的例子,将实现XML文档转换为XHTML然后在Web浏览器中显示
XML文档为:DVD.xml
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="XSLDemo.xsl"?><!--引入XSL样式表--> <library> <DVD id="1"> <title>Breakfast at Tiffany's</title> <format>Movie</format> <genre>Classic</genre> </DVD> <DVD id="2"> <title>Contact</title> <format>Movie</format> <genre>Science fiction</genre> </DVD> <DVD id="3"> <title>Little Britain</title> <format>TV Series</format> <genre>Comedy</genre> </DVD> </library>
XSL样式表
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" version="4.0"/> <!--输出的目标文档为html--> <xsl:template match="/"><!--匹配根元素--> <html> <head> <title>DVD Library Listing</title> </head> <body> <table width="40%" border="1"> <tr> <th>Title</th> <th>Format</th> <th>Genre</th> </tr> <xsl:for-each select="/library/DVD"><!--遍历每个DVD元素,通过XPATH选择节点--> <xsl:sort select="genre"/><!--按照genre列排序--> <tr> <td><xsl:value-of select="title"/></td> <td><xsl:value-of select="format"/></td> <td><xsl:value-of select="genre"/></td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
用浏览器打开XML文件会看到使用样式后的效果。
6. XPATH
XPATH用于定位XML文档的特定部分,可以通过XPATH表达式得到一个单独的节点,一组节点等等,从而避免遍历DOM树节点进行查找。XPATH将XML文档看作是由节点构成的层次树,每棵树包括元素节点、属性节点、文本节点、处理指令、注释和命名空间,通过相应的路径来定位相应的节点。根节点是XML文档树的起始点,一个XML文档就是根节点,"/"代表根节点。下面以DVD.xml为例,接单介绍一下XPATH的使用。
(1) /library/DVD
定位library的所有DVD子元素,得到一组节点
(2) /library/DVD[2]
定位library的第2个DVD子元素,得到一个节点
(3) /library/DVD/@id
得到所有DVD的id属性
(4) /library/DVD[2]/@id
得到第2个DVD的id属性
(5) /library/DVD[genre='Comedy']
过滤条件,定位genre等于Comedy的DVD
(6) count(/library/DVD)
得到DVD的数量
JDK5.0中增加了API来处理XPATH,下面例子是通过XPATH来查询DVD.xml
/** * */ package com.killer.xml; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * @author chosen0ne * * 2010-6-10 下午03:52:29 */ public class XPathTest { /** * @param args * @throws ParserConfigurationException * @throws IOException * @throws SAXException * @throws XPathExpressionException */ public static void main(String[] args) throws ParserConfigurationException, IOException, XPathExpressionException { // TODO Auto-generated method stub String file="xml/XSLDemo.xml"; XPathFactory factory=XPathFactory.newInstance(); XPath path=factory.newXPath(); InputSource source=new InputSource(new FileInputStream(file)); String query="/library/DVD"; NodeList nodeList=(NodeList) path.evaluate(query, source,XPathConstants.NODESET); System.out.println(query+" 结果节点的个数:"+nodeList.getLength()); for(int i=0;i<nodeList.getLength();i++){ Node n=nodeList.item(i); Node attrNode=n.getAttributes().getNamedItem("id"); System.out.println(n.getNodeName()+" "+attrNode.getNodeName()+" "+attrNode.getNodeValue()); } source=new InputSource(new FileInputStream(file));//需要重新读取XML文件,否则出现reader error query="/library/DVD[2]"; Node node=(Node) path.evaluate(query, source, XPathConstants.NODE); System.out.println(query+": "+node.getNodeName()+" "+node.getAttributes().getNamedItem("id").getNodeValue()); source=new InputSource(new FileInputStream(file)); query="/library/DVD[genre='Comedy']"; node=(Node) path.evaluate(query, source, XPathConstants.NODE); System.out.println(query+": "+node.getNodeName()+" "+node.getAttributes().getNamedItem("id").getNodeValue()); source=new InputSource(new FileInputStream(file)); query="/library/DVD/@id"; nodeList=(NodeList) path.evaluate(query, source, XPathConstants.NODESET); System.out.println(query+": 结果节点个数"+nodeList.getLength()); for(int i=0;i<nodeList.getLength();i++){ Node n=nodeList.item(i); System.out.println(n.getNodeName()+" "+n.getNodeValue()); } source=new InputSource(new FileInputStream(file)); query="count(/library/DVD)"; double count=(Double)path.evaluate(query, source, XPathConstants.NUMBER); System.out.println(query+": "+count); } }
1. XML文档处理模型
应用程序需要通过XML处理程序从XML文档中提取信息。XML处理程序通常被称为XML解析器。
XML有两种处理模式:基于树和基于事件。基于树的解析器通常称为DOM(Document Object Model,文档对象模型)解析器,而基于事件的解析器通常称为SAX(Simple API for XML,XML简单应用编程接口)解析器。DOM是W3C推荐的标准,允许通过编程语言或脚本语言(JavaScript)访问这些元素以及它们的值。SAX是以一串事件的形式展现XML文档,必须为每个事件(比如,开始或是结束某个元素)编写 处理程序,当事件触发处理程序会产生相应的结果,由于SAX是以事件处理机制为基础的,所以在有良好事件处理机制的语言中很实用。
2. SAX和DOM比较
(1). DOM提供了对整个XML文档的完整的读写访问,并且可以通过遍历文档树对文档内的节点进行访问。基于DOM的解析会将整个XML读入内存,在内存中构造整棵XML树,所以当遇到较大XML文档时,DOM解析会变慢。
(2). SAX是串行操作的,一个节点被处理后就被丢弃了,并且不会再被处理。整个文档不是一次性读入内存,从而避免了较大XML文档引起的处理问题。如果只是对某些节点感兴趣,而不关心其上下文,这种情况应该用SAX(例如,网络爬虫只关心<a>标签所以适合用SAX解析)。但是,SAX模型不会记录已经丢弃的节点的信息,所以必须由开发人员自己来维护XML文档中的可供后续使用的信息。从而SAX使用起来比较繁琐。
3. 小结
DOM提供了对XML的读写,XPATH查询,文档验证等功能,SAX只是提供简单的解析功能。SAX和DOM属于操作XML文档较低层的类库,目前已有很多类库实现了这两种方式,并对其进行封装,使用户用起来更加方便。比如Dom4J,JDom等。下文将对Dom4J进行介绍。
SAX解析器在解析XML文件时,如果遇到XML构件(例如:标签)就会触发相应的事件,但它不会以任何方式存储文档,由事件处理程序决定是否建立数据结构存储信息。实际上,DOM解析器是建立在SAX解析器的基础之上的,在读入XML信息时接受相应的时间然后建立DOM树。
在使用SAX解析器时,需要建立一个事件处理器来处理不同事件。ContentHandler接口定义了多个回调方法来处理相应的事件,其中比较重要的方法有:
startDocument() 在文档开始时调用
endDocument() 在文档结束时调用
startElement(String uri, String localName, String qName, Attributes attributes) 在元素开始时调用
endElement(String uri, String localName, String qName) 在元素结束时调用
characters(char[] ch, int start, int length) 每当遇到字符数据时调用
处理器必须覆盖这些方法,完成在解析文件时要执行的动作。
这里给出示例程序,打印一个XHTML文件中的所有<a href=...>元素:
package com.killer.xml; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /** * @author chosen0ne * * 2010-6-8 下午04:59:37 */ public class SAXTest { /** * 打印一个XHTML文件中的所有超链接 * @param filePath * @throws SAXException * @throws ParserConfigurationException * @throws IOException * @throws FileNotFoundException */ public static void printATag(String filePath) throws ParserConfigurationException, SAXException, FileNotFoundException, IOException { SAXParserFactory factory=SAXParserFactory.newInstance(); factory.setNamespaceAware(true);//打开命名空间处理特性 SAXParser parser=factory.newSAXParser(); XMLReader reader=parser.getXMLReader(); reader.setContentHandler(new DefaultHandler(){ /** * uri 命名空间 * localName 本地名 * qName alias:localName的限定名,随时可用 * 如果命名空间处理特性打开,则uri和localName可用,否则都为空 * SAXParserFactory.setNamespaceAware(true)将命名空间处理特性打开 */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // TODO Auto-generated method stub super.startElement(uri, localName, qName, attributes); if(localName.equalsIgnoreCase("a")&&attributes!=null){ String href=attributes.getValue("href"); if(href!=null) System.out.println(href); } } }); reader.parse(new InputSource(new FileInputStream(filePath))); } /** * @param args * @throws SAXException * @throws ParserConfigurationException * @throws IOException * @throws FileNotFoundException */ public static void main(String[] args) throws ParserConfigurationException, SAXException, FileNotFoundException, IOException { // TODO Auto-generated method stub printATag("xml//XHTMLDemo.xml"); } }
DOM(Document Object Model, 文档对象模型)解析器将读入的XML文档转化为树结构。DOM解析器的接口已经被W3C标准化,org.w3c.dom包中包含了所有的接口,例如:Document和Element等。Document对象是文档树在内存中的表现,可以通过它访问该树的节点,它由实现Node接口及其多个子接口的对象构成。接口Node的层次结构如下:
下面例子是通过DOM操作DVD.xml显示详细信息:
/** * */ package com.killer.xml; import java.io.File; 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; /** * @author chosen0ne * * 2010-6-9 下午08:34:24 */ public class DomTest { /** * @param args * @throws ParserConfigurationException * @throws IOException * @throws SAXException */ public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { // TODO Auto-generated method stub DocumentBuilder builder=DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc=builder.parse(new File("xml/XSLDemo.xml")); Element root=doc.getDocumentElement(); NodeList children=root.getChildNodes(); for(int i=0;i<children.getLength();i++){ Node n=children.item(i); if(n instanceof Element){ NamedNodeMap attr=n.getAttributes(); System.out.println("DVD "+attr.getNamedItem("id").getNodeValue()); NodeList dvdItems=n.getChildNodes(); for(int j=0;j<dvdItems.getLength();j++){ Node item=dvdItems.item(j); if(item instanceof Element){ System.out.println(item.getNodeName()+":"+item.getTextContent()); } } } } } }
Dom4J对SAX和DOM进行了封装,使其用起来更加方便。
/** * */ package com.killer.xml; import java.io.File; 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.Node; import org.dom4j.io.SAXReader; /** * @author chosen0ne * * 2010-6-8 下午04:11:46 */ public class Dom4JTest { /** * @param args */ @SuppressWarnings("unchecked") public static void main(String[] args) { // TODO Auto-generated method stub String path="xml//XSLDemo.xml"; SAXReader reader=new SAXReader(); try { Document doc=reader.read(new File(path)); Element rootElement=doc.getRootElement(); System.out.println(rootElement.getName()+"--"+rootElement.getTextTrim()); visitElement(rootElement); //以xpath方式访问节点 System.out.println("以xpath形式访问节点"); List<Node> nodes=doc.selectNodes("/library/DVD/title"); for(Node n: nodes){ System.out.println(n.getName()+":"+n.getText()+""); } } catch (DocumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @SuppressWarnings("unchecked") public static void visitAttribute(Element el) { Iterator<Attribute> iterator=el.attributeIterator(); while(iterator.hasNext()){ Attribute attribute=iterator.next(); System.out.println(attribute.getName()+"--"+attribute.getText()); } } @SuppressWarnings("unchecked") public static void visitElement(Element el) { Iterator<Element> iterator=el.elementIterator(); while(iterator.hasNext()){ Element element=iterator.next(); System.out.println("Element:"); System.out.println(element.getName()+"-"+element.getTextTrim()); System.out.println("Attribute:"); visitAttribute(element); visitElement(element); } } }