核心技术Ⅱ:XML
XML概述
XML 是一种非常有用的描述结构化信息的技术。
“Properties”类:在单个方法调用中读入这样的“属性文件”(property file):
- 属性文件:用于描述程序配置;包含了一组“名/值”对:
fontname=Times Roman fontsize=l2 windowsize=400 200 co1or=O 5O 100
但是:
- 属性文件采用的是一种单一的平面层次结构;所以常常会出现这种情况:
title.fontname=Helvetica title.fontsize=36 body.fontname=Times Roman body.fontsize=12
- 属性文件要求键是唯一的;所以保存序列值需要:
menu.item.l=Times Roman menu.item.2=Helvetica menu.item.3=Goudy Old Style
XML 和HTML 格式是古老的标准通用标记语言(Standard Generalized Markup Language, SGML) 的衍生语言。
- XML 格式能够表达层次结构,并且重复的元素不会被曲解:
<configuration>
<title>
<font>
<name>Helvetica</name>
<Size>36</size>
</font>
</title>
<body>
<font>
<name>Times Roman</name>
<Size>12</size>
</font>
</body>
<window>
<width>400</width>
<height>200</height>
</window>
<color>
<red>O</red>
<green>50</green>
<blue> 100</blue>
</color>
<menu>
<item>Times Roman</item>
<item>Helvetica</item>
<item>Goudy 01d Style</item>
</menu>
</configuration>
XML 和HTML的区别:
XML | HTML |
---|---|
大小写敏感
如:<H1> 和<hl> 是不同的XML 标签 |
不敏感 |
结束标签绝对不能省略 | 如果从上下文中可以分清哪里是段落或列表项的结尾,那么结束标签(如</p>或</li>) 就可以省略 |
只有单个标签而没有相对应的结束标签的元素必须以“/”结尾
如:<img src="coffeecup.png"/ > |
|
属性值必须用引号括起来
如:<applet code="MyApplet.class" width="300" height="300"> |
引号是可有可无的
如:<applet code="MyApplet.class" width=300 height=300> |
所有属性必须都有属性值
如:<input type="radio" name="1anguage" value="Java" checked="checked"> |
属性名可以没有值
如:<input type="radio" name="1anguage" value="Java" checked> |
XML 文档的结构
- XML 文档应当以一个文档头开始:
<?xml version="l.O"?> 或 <?xml version="l.O" encoding="UTF-8"?>
- 文档头之后通常是文档类型定义(Docun1ent Type Definition, DTD):
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//OTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
- XML 文档的正文包含根元素,根元素包含其他元素:
<?xml version="l.O"?> <!DOCTYPE configuration ... > <configuration> <title> <font> <name>Helvetica</name> <Size>36</size> </font> </title> ... </configuration>
- 元素可以有子元素(child element )、文本或两者皆有;
- 在设计XML 文档结构时,应避免混合式内容(mixed content),以简化解析过程:
<font> Helvetica <Size>36</size> </font>
- XML元素可以包含属性,但属性的灵活性要差很多;
- “属性只应该用来修改值的解释,而不是用来指定值。”
- “HTML 中,凡是不显示在网页上的都是属性。”
<font name="Helvetica" size="36 pt"/> <font> <name>Helvetica</name> <Size unit="pt">36</size> </font>
XML中的其他一些标记:
- 字符引用(character reference):形式是“&#”十进制值;或“&#x”十六进制值;
- 实体引用(entity reference):形式是“&name”;
下面这些实体引用: < > & " ' 都有预定义的含义: 小于、大于、&、引号、省略号等字符。
- CDATA部分(CDATA Section):用“<![CDATA[”和“]]>”来限定其界限;
- 它们是字符数据的一种特殊形式。可以使用它们来襄括那些含有“<”、“>”、“&”之类字符的字符串,而不必将它们解释为标记;
- CDATA 部分不能包含字符串“]]>”;
<![CDATA[< & > are my favorite delimiters]]>
- 处理指令(processing instruction):是那些专门在处理XML 文档的应用程序中使用的指令,它们由“<?”和“?>”来限定其界限:
<?xml-stylesheet href="mystyle.css" type="text/css"?> 每个XML 都以一个处理指令开头: <?xml version="l.O"?>
- 注释(comment):用“<!-”和“-->”限定其界限:
- 注释不应该含有字符串“--”;
<!-- This is a comment. -->
解析XML文档
xml解析器:读入一个文件,确认这个文件具有正确的格式(验证xml文档),然后将其分解成各种元素,使得程序员能够访问这些元素;
- Java 库提供了两种XML 韶析器:
- “树型解析器”:将读入的XML 文档转换成树结构。
- 如:文档对象校型(Document Object Model, DOM)解析器;
- “流机制解析器”:在读入XML 文档时生成相应的事件。
- 如:XML 简单API(Simple API for XML, SAX)解析器;
- “树型解析器”:将读入的XML 文档转换成树结构。
- 何时使用“流机制解析器”:
- 如果需要处理很长的文档,若生成树结构将会消耗大量内存;
- 或者如果只对某些元素感兴趣,而不关心它们的上下文;
树型解析器
DOM 韶析器的接门已经被W3C 标准化了。org.w3c.dom 包中包含了这些接口类型的定义,比如: Document 和Element 等。不同的提供者,比如Apache 组织和IBM, 都编写了实现这些接口的DOM 解析器。
Java XML 处理 API (Java API for XML Processing, JAXP) 库使得我们实际上可以以插件形式使用这些韶析器中的任意一个。
- JDK 中也包含了从Apache 解析器导出的DOM 解析器;
读人XML文档:
- 首先需要“DocumentBuilder”对象(从“DocumentBuilderFactory”中得到):
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder();
- 从不同的源中读入文档:
// 从文件: File f = ... Document doc = builder.parse(f); // 从一个URL: URL u = ... Document doc= builder.parse(u); // 从指定的输入流: InputStream in = ... Document doc= builder.parse(in);
- 果使用输入流作为源,那么对于那些以该文档的位置为相对路径而被引用的文档,解析器将无法定位。
解析文档内容:
- 调用“getDocumentElement”方法,获取文档根元素:
- 调用“getTagName”方法可以返同元素的标签名;
Element root = doc.getDocumentElement(); String rootName = root.getTagName();
- 调用“getChildNode”方法,获取元素的子元素(可能是子元素、文本、注释或其他节点):
- 返回一个类型为“Nodelist”的集合;
- “item”方法将得到指定索引俏的项;
- “getLength”方法则提供了项的总数;
// 枚举所有子元素 // 得到的子元素会包含:元素之间的“\n”等空白字符 Nodelist children = root.getChildNodes(); for (int i = O; i < children.getlength(); i++) { Node child= children.item(i); ... } // 枚举所有子元素 // 并忽略元素之间的“\n”等空白字符 Nodelist children = root.getChildNodes(); for (int i = O; i < children.getlength(); i++) { Node child= children.item(i); if (chi1d instanceof Element) { Element childElement = (Element) child; ... } }
- 利用DTD,解析器会忽略空白字符;
- 获取节点文本:
- 使用“getFirstChild”方法而不用再遍历另一个Nodelist;
- 使用“getData”方法获取存储在Text节点中的字符串;
for (inti = O; i < children.getlength(); i++) { Node child = chi1dren.item(i); if (child instanceof Element) { Element childElement = (Element) chi1d; Text textNode = (Text) childElement.getFirstChild(); String text = textNode.getData().trim(); if (childElement.getTagName().equals("name")) name = text; else if (childElement.getTagName().equals("size")) size = Integer.parseInt(text); } }
- 也可以用“getlastChild”方法得到最后一项子元素;
- 用“getNextSibling”得到下一个兄弟节点;
// 另一种遍历子节点集的方法 for (Node childNode = element.getFirstChild(); childNode != null; childNode = childNode.getNextSibling()) { ... }
- 获取元素属性:
- 调用“getAttributes”方法,枚举节点的属性:
- 返回一个“NamedNodeMap”对象,其中包含了描述屈性的“Node”对象。可以和“Nodelist”一样的方式遍历;
- 调用“getNodeName”和“getNodeValue”方法可以得到属性名和属性值;
NamedNodeMap attributes = element.getAttributes(); for (int i = O; i < attributes.getlength(); i++) { Node attribute = attributes.item(i); String name = attribute.getNodeName(); String value= attribute.getNodeValue(); ... }
- 如果知道属性名,则可以直接获取相应的属性值:
String unit = element.getAttribute("unit");
- 调用“getAttributes”方法,枚举节点的属性:
相关方法
javax.xml.parsers.DocumentBuilderFactory 1.4
- static DocumentBuilderFactory newlnstance()
- 返回DocumentBuilderFactory 类的一个实例。
- DocumentBuilder newDocumentBuilder()
- 返回DocumentBuilder 类的一个实例。
javax.xml.parsers.DocumentBuilder 1.4
- Document parse( File f)
- Document parse( String url)
- Document parse( InputStream in)
- 解析来自给定文件、URL 或输入流的XML 文档,返回解析后的文档。
org.w3c.dom.Document 1.4
- Element getDocumentElement()
- 返回文挡的根元素。
org.w3c.dom.Element 1.4
- String getTagName()
- 返回元素的名字。
- String getAttribute( String name)
- 返回给定名字的属性值,没有该属性时返回空字符串。
org.w3c.dom.Node 1.4
- Nodelist getChildNodes()
- 返回包含该节点所有子元素的节点列表。
- Node getFirstChi1d()
- Node getLastChi1d()
- 获取该节点的第一个或最后一个子节点,在该节点没有子节点时返回null 。
- Node getNextSibling()
- Node getPreviousSibling()
- 获取该节点的下一个或上一个兄弟节点,在该节点没有兄弟节点时返回null 0
- Node getparentNode()
- 获取该节点的父节点,在该节点是文档节点时返回null 。
- NamedNodeMap getAttributes()
- 返回含有描述该节点所有属性的Attr 节点的映射表。
- String getNodeName()
- 返回该节点的名字。当该节点是Attr 节点时, 该名字就是属性名。
- String getNodeValue()
- 返回该节点的值。当该节点是Attr 节点时,该值就是属性值。
org.w3c.dom.CharacterDats 1.4
- String getData()
- 返回存储在节点中的文本。
org.w3c.dom.NodeList 1.4
- int getlength() ..,
- 返回列表中的节点数。
- Node item(int index)
- 返回给定索引值处的节点。索引值范围在0 到getlength()-1 之间。
org.w3c.dom.NamedNodeMap 1.4
- int getlength ( )
- 返回该节点映射表中的节点数。
- Node item( int index)
- 返回给定索引值处的节点。索引值范围在0 到getlength()-1 之间。
流机制解析器
对于大多数应用, DOM 都运行得很好。但是,如果
- 文档很大,并且处理算法又非常简单,
- 可以在运行时解析节点,而不必看到完整的树形结构,
那么DOM 可能就会显得效率低下。在这种情况下,我们应该使用流机制解析器(streaming parser):
- (解析某些节点,而非整个树)
- SAX 解析器
- 使用事件回调(eventcallback)
- StAX 解析器;
- Java SE 6 中添加;
- 提供了遍历解析事件的迭代器;
使用“SAX”解析器
- 实际上, DOM 解析器是在SAX 解析器的基础上构建的,它在接收到解析器事件时构建DOM 树。
- HTML不必是合法的XML,但 XHTML 是一种HTML方言,且是良构的XML。
使用SAX 解析器时,需要一个处理器来为各种解析器事件定义事件动作。“ContentHandler”接口定义了若干个在解析文档时解析器会调用的回调方法,下面是最重要的几个:
- “startDocument”和“endDocument”分别在文档开始和结束时各调用一次。
- “startElement”和“endElement”在每当遇到起始或终止标签时凋用。
- “characters”在每当遇到字符数据时调用。
代码:
- 得到SAX 解析器:
SAXParserFactory factory= SAXParserFactory.newInstance(); factory.setNamespaceAware(true); // 设置打开命名空间处理特性(默认是关闭的) factory.setFeature("http://apache.org/xml/features/nonvalidating/1oad-external-dtd", false); // 设置不需要验证DTD文件 SAXParser parser = factory.newSAXParser();
- 处理文档:
parser.parse(source, handler);
- 其中:
- source 可以是一个文件、一个URL 字符串或者是一个输人流;
- handler 属于“DefaultHandler”的一个子类;
- “DefaultHandler”类为以下四个接口定义了空的方法:
- “ContentHandler”
- “DTDHandler”
- “EntityResolver”
- “ErrorHandler”
示例:
package sax; import java.io.*; import java.net.*; import javax.xml.parsers.*; import org.xml.sax.*; import org.xml.sax.helpers.*; public class SAXTest { public static void main(String[] args) throws Exception { String url; if (args.1ength = O) { url = "http://www.w3c.org"; System.out.println("Using" + url); } else url = args[0] ; InputStream in = new URL(url).openStream(); DefaultHandler handler = new DefaultHandler() { public void startflement(String namespaceURI, String lname, String qname, Attributes attrs) { if (lname.equa1s("a") && attrs != null) { for (int i = 0; i < attrs.getlength(); i++) { String aname = attrs.getlocalName(i); if (aname.equals ("href")) System.out.println(attrs.getValue(i)) ; } } } }; SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); factory.setFeature("http://apache.org/xml/features/nonvalidating/1oad-external-dtd", false); SAXParser saxParser = factory.newSAXParser(); saxParser.parse(in, handler); } }
相关方法
javax.xml.parsers.SAXParserFactory 1.4
- static SAXParserFactory newlnstance()
- 返回SAXParserFactory 类的一个实例。
- SAXParser newSAXParser()
- 返回SAXParser 类的一个实例。
- boolean isNamespaceAware()
- void setNamespaceAware(boolean va1ue)
- 获取和设置工厂的namespaceAware 属性。当设为true 时,该下厂生成的解析器是命名空间感知的。
- boolean isValidating()
- void setValidating(boolean value)
获取和设置工厂的validating 屈性。当设为true 时,该工厂生成的解析器将要验证其输入。 javax.xml.parsers.SAXParser 1.4
- void parse(File f, DefaultHandler handler)
- void parse(String url, DefaultHandler handler)
- void parse(InputStream in, DefaultHandler handler)
- 解析来自给定文件、URL 或输入流的XML 文档,并把解析事件报告给指定的处理器。
arg.xml.sax.ContentHandler 1.4
- void startDocument()
- void endDocument()
- 在文档的开头和结尾处被调用。
- void startElement(String uri, String lname, String qname, Attributes attr)
- void endElement(String uri, String lname, String qname)
- 在元素的开头和结尾处被调用。
- 参数:
- uri 命名空间的URI (如果解析器是命名空间感知的)
- lname 不带前缀的本地名(如果解析器是命名空间感知的)
- qname 元素名(如果斛析器是命名空间感知的),或者是带有前缀的限定名
- (如果解析器除了报告本地名之外还报告限定名)
- void characters(char[] data, int start, int length)
- 解析器报告字符数据时被调用。
- 参数:
- data 字符数据数组
- start 在作为被报告的字符数据的一部分的字符数组中,第一个字符的索引
- length 被报告的字符串的长度
org.xml.sax.Attributes 1.4
- int get Length()
- 返回存储在该屈性集合中的属性数认。
- String getlocalName(int index)
- 返回给定索引的属性的本地名(无前缀),或在解析器不是命名空间感知的悄况下返回空字符串。
- String getURI(int index)
- 返回给定索引的屈性的命名空间URI, 或者,当该节点不是命名空间的一部分,或解析器并非命名空间感知时返同空字符串。
- String getQName(int index)
- 返回给定索引的屈性的限定名(带前缀),或当解析器不报告限定名时返回空字符串。
- String getValue(int index)
- String getValue(String qname)
- String getValue(String uri, String lname)
- 根据给定索引、限定名或命名空间URI+ 本地名来返回属性值;当该值不存在时,返回null 。
使用“StAX”解析器
StAX 解析器是一种“拉解析器(pull parser)”,与安装事件处理器不同,只需使用下面这样的基本循环来迭代所有的事件:
InputStream in = url.openStream(); XMLInputFactory factory = XMLInputFactory.newInstance(); factory.setProperty(XMLinputFactory.IS_NAMESPACE_AWARE, false); // 设置关闭命名空间处理特性(默认是打开的) XMLStreamReader parser= factory.createXMLStreamReader(in); whi1e(parser.hasNext()) { int event = parser.next(); Call parser methods to obtain event details }
- 获取当前元素的units 属性:
String units= parser.getAttributeValue(null, "units");
示例:
package stax;
import java.io.*;
import java.net.*;
import javax.xml.stream.*;
puhlic class StAX.Test {
pub1ic static void main(String[] args) throws Exception {
String urlString;
if (args.1ength == 0) {
urlString = "http://www.w3c.org";
System.out.println("Using" + urlString);
}
e1se urlString = args[0];
URL url = new URL(urlString);
InputStream in = url.openStream();
XMLInputFactory factory =XMLInputFactory.newlnstance();
XMLStreamReader parser = factory.createXMLStreamReader(in);
while (parser.hasNext()) {
int event = parser.next();
if (event= XMLStreamConstants.START_ELEMENT) {
if (parser.getlocalName().equals("a")) {
String href = parser.getAttributeValue(null, "href");
if (href != null)
System.out.println(href);
}
}
}
}
}
相关方法
javax.xml.stream.XMLlnputFactory 6
- static XMLinputFactory newlnstance()
- 返回XMLinputFactory 类的一个实例。
- void setProperty(String name, Object value)
- 设置这个工厂的属性,或者在要设笠的屈性不支持设置成给定值时,抛出I11ega1ArgumentException 。Java SE 的实现支待下列Boolean 类型的属性:
- "javax.xml.stream.isValidating" 为false(默认值)时,不验证文档(规范不要求必须支持)。
- "javax.xml.stream.isNamespaceAware" 为true(默认值)时,将处理命名空间(规范不要求必须支持)。
- "javax.xml.stream.isCoalescing" 为fa1se(默认值)时,邻近的字符数据不进行连接。
- "javax.xml.stream.isReplacingEntityReferences" 为true(默认值)时实体引用将作为字符数据被替换和报告。
- "javax.xml.stream. isSupportingExtemalEntities" 为true(默认值)时,外部实体将被解析。规范对于这个属性没有给出默认值。
- "javax.xml.stream.supportDTD" 为true(默认值)时, DTD 将作为事件被报告。
- XMLStreamReader createXMLStreamReader(InputStream in)
- XMLStreamReader createXMLStreamReader(InputStream in, StringcharacterEncoding)
- XMLStreamReader createXMLStreamReader(Reader in)
- XMLStreamReader createXMLStreamReader(Source in)
- 创建一个从给定的流、阅读器或JAXP 源读人的解析器。
javax.xml.stream.XMLStreamReader6
- boolean has Next()
- 如果有另一个解析事件,则返回true 。
- int next()
- 将解析器的状态设赏为下一个解析事件,并返回下列常散之一: START_ELEMENT、END_ELEMENT、CHARACTERS、START_DOCUMENT、END_DOCUMENT、CDATA、COMMENT、SPACE(可忽略的空白字符)、PROCESSING_INSTRUCTION、ENTITY_REFERENCE、DTD
- boolean isStartElement()
- boolean i sEndEl ement()
- boolean i sCharacters()
- boolean i sWhi teSpace()
- 如果当前事件是一个开始元索、结束元素、字符数据或空门字符.则返回true 。
- QName getName()
- String getLocalName()
- 获取在START_ELEMENT 或END_ELEMENT 事件中的元素的名字。
- String getText()
- 返回一个CHARACTERS 、COMMENT 或CDATA 事件中的字符,或一个ENTITY_REFERENCE的替换值,或者一个DTD 的内部子梨。
- int getAttributeCount()
- QName getAttributeName(int index)
- String getAttributeLocalName(int index)
- String getAttributeValue(int index)
- 如果当前事件是START_ELEMENT, 则获取屈性数址和屈性的名字与值。
- String getAttri buteVa l ue(String namespaceURI, String name)
- 如果当前事件是START_ELEMENT, 则获取具有给定名称的属性的值。如果namespaceURI为null, 则不检查名字空间。
使用“XPath”
XPath 语言使得访问树节点变得很容易,而不必遍历DOM树。
如,对于以下XML文档:
<configuration>
...
<database>
<username>dbuser</username>
<password>secret</password>
...
</database>
</configuration>
可以通过对XPath 表达式“/configuration/database/username”求值,来得到database中的username的值。
XPath表达式
- XPath 可以描述XML 文档中的个节点集:
/gridbag/row 描述了根元素gridbag 的子元素中所有的row 元素
- 可以用“[]”操作符来选择特定元素:
/gridbag/row[l] 表示的是第一行(索引号从1 开始)
- 使用“@”操作符可以得到属性值:
/gridbag/row(1)/ce11[1]/@anchor 描述了作为根元索gridbag 的子元素的那些row 元素中的所有单元格的anchor 属性节点
- XPath 有很多有用的函数:
- 参见http://www.w3c.org/TR/xpath的规范;
- 或者http://www.zvon.org/xxl/XPatbTutorial/General/exampleshtml上的在线指南;
count(/gridbag/row) 返回gridbag 根元素的row 子元素的数量
使用XPath
Java SE 5.0 增加了一个API 来计算XPath 表达式:
- 首先需要从“XPathFactory”创建一个“XPath”对象:
XPathFactory xpfactory = XPathFactory.newlnstance(); path = xpfactory.newXPath();
- 然后,调用“evaluate”方法来计算XPath 表达式:
- (可以用同一个XPath 对象来计算多个表达式)
- 如果返回一个字符串,不用指定“resultType”(“XPathConstants”常量):
String username = path.evaluate("/configuration/database/username", doc);
- 如果结果是一组节点,则使用“XPathConstants.NODESIT”:
NodeList nodes = (NodeList) path.evaluate("/gridbag/row", doc, XPathConstants.NODESIT);
- 如果结果只有一个节点,则以“XPathConstants.NODE”代替:
Node node = (Node) path.evaluate("/gridbag/row[l)", doc, XPathConstants.NODE);
- 如果结果是一个数字,则使用“XPathConstants.NUMBER”:
int count = ((Number) path.eva1uate("count(/gridbag/row)", doc, XPathConstants.NUMBER)).intVa1ue();
- 不必从文档的根节点开始搜索,可以从任意一个节点或节点列表开始。
- 如果有前一次计算得到的节点,那么就可以调用:
result = path.evaluate(expression, node);
相关方法
javax.xml.xpath.XPathFactory 5.0
- static XPathFactory newinstance()
- 返同用千创建XPath 对象的XPathFactory 实例。
- XPath newXpath()
- 构建用于计算XPath 表达式的XPath 对象。
javax.xml.xpath.XPath 5.0
- String evaluate(String expression, Object startingPoint)
- 从给定的起点计算表达式。起点可以是一个节点或节点列表。如果结果是一个节点或节点集, 则返回的字符串由所有文本节点子元素的数据构成。
- Object eva1uate(String expression , Object startingPoint, QName resultType)
- 从给定的起点计算表达式。起点可以是一个节点或节点列表。resultType 是XPathConstants 类的常量STRING 、NODE 、NODESET 、NUMBER 或BOOLEAN 之一。
- 返回值是String 、Node 、Nodelist 、Number 或Boolean 。
使用命名空间
关于
命名空间(namespace) 机制,用于元素名和属性名,可以避免名字冲突:
- 名字空间是由统一资源标识符(Uniform Resource Identifier, URI ) 来标识的:
http://www.w3.org/2001/XMLSchema uuid:lc759aed-b748-47Sc-ab68-10679700c4f2 urn:com:books-r-us
- HTTP 的URL 格式是最常见的标识符。
- URL 只用作标识符字符串,而不是一个文件的定位符。
- 如下,表示了不同的Namespace,但在Web服务器中对应于同一个文档:
http://www.horstmann.com/corejava http://www.horstmann.com/corejava/index.html
- 在命名空间的URL 所表示的位性上不需要有任何文档,XML 解析器不会尝试去该处查找任何东西。
Namespace机制:(xml中用“xmlns”标识Namespace)
- 嵌套的Namespace:
<element xmlns="namespaceURil"> <child xmlns="namespaceURI2"> grandchildren </child> more children </element> 第一个子元素和孙元索都是第二个命名空间的一部分
- 前缀表示Namespace:
<xsd:schema xm1ns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="gridbag" type="GridBagType"/> ... </xsd:schema> 前缀“xsd”标识“http://www.w3.org/2001/XMLSchema”中的元素,如“xsd:schema”
- 有子元素继承了它们父元素的命名空间,而不带显式前缀的属性并不是命名空间的一部分:
<configuration xmlns="http://www.horstmann.com/corejava" xmlns:si="http://www.bipm.fr/enus/3_SI/si.html"> <size value="210" si:unit="mm"/> ... </configuration> 1、元素“configuration”和“size”是URI 为“http://www.horstmann.com/corejava”的命名空间的一部分; 2、属性“si:unit”是URI 为“http://www.bipm.fr/enus/3_ SI/si.html”的命名空间的一部分。 3、属性“value”不是任何命名空间的一部分
使用
- 默认悄况下,Java XML 库的DOM 解析器并非“命名空间感知的”。
调用“DocumentBuilderFactory”类的“setNamespaceAware”方法,打开命名空间处理特性:
factory.setNamespaceAware(true);
这样,该工厂产生的所有生成器便都支持命名空间了。每个节点有三个属性:
- 带有前缀的限定名(qualified),由“getNodeName”和“getTagName”等方法返回。
- 命名空间URI,由“getNamespaceURI”方法返回。
- 不带前缀和命名空间的本地名(localname),由“getlocalName”方法返回。
- 如果对命名空间的感知特性被关闭,“getLocalName”和“getNamespaceURI”方法将返回“null”;
如:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
会报告如下信息:
- 限定名 = “xsd:schema”;
- 命名空间URI = “http://www.w3.org/2001/XMLSchema”;
- 本地名 = “schema”;
相关方法
org.w3c.dom.Node 1.4
- String getLocalName()
- 返回本地名(不带前缀),或者在解析器不感知命名空间时,返回null 。
- String getNamespaceURI()
- 返回命名空间URI, 或者在解析器不感知命名空间时,返回null 。
javax.xml.parsers.DocumentBuilderFactory 1.4
- boolean isNamespaceAware( )
- void setNamespaceAware(boolean value)
- 获取或设置工厂的namespaceAware 属性。当设为true 时,工厂产生的解析器是命名空间感知的。
验证XML文档
- XML 解析器的一个很大的好处就是它能自动校验某个文档是否具有正确的结构。
指定文档结构,可以提供一个文档类型定义( DTD )或一个XML Schema 定义。
- DTD 或schema 包含了用于解释文档应如何构成的规则,这些规则指定了每个元素的合法子元素和属性。
- XML Schema 可以表达更加复杂的验证条件(比如size 元素必须包含一个整数)。XML Schema 自身使用的就是XML。
文档类型定义
引入DTD的方式
提供DTD 的方式有多种:
- 文档类型必须匹配根元素的名字,如以下的configuration。
- 纳入到XML 文档中:
<?xml version="l.O"?> <!OOCTYPE configuration [ <!ELEMENT configuration . . . > more rules . . . ]> <configuration> . . . </configuration>
- 指定一个包含DTD 的URL:
- 如果使用的是DTD 的相对URL(比如“config.dtd”),那么要给解析器一个“File”或“URL”对象,而不是“InputStream”。
<!DOCTYPE configuration SYSTEM "config.dtd"> 或 <!DOCTYPE configuration SYSTEM "http://myserve.com/config.dtd"〉
- 如果XML 处理器知道如何定位带有公共标识符的DTD,那么就不需要URL了:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web App1ication 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd”>
- 如果使用DOM解析器,且要支持“PUBLIC”标识符,则需调用“DocumentBuilder”类的“setEntityResolver”方法来安装“EntityResolver”接口的某个实现类的一个对象。
- (该接口只有一个方法:“resolveEntity”)
- 典型实现的代码框架:
class MyEntityResolver implements EntityResolver { public InputSource resolveEntity(String publicID, String systemID) { if (publicID.equals(a known ID)) return new InputSource(DTD data); else return null; // use default behavior } }
元素规则
ELEMENT规则用于指定某个元素可以拥有什么样的子元素:(可以指定一个正则表达式)
如:
- menu 元素包含0 或多个item 元素:
<!ELEMENT menu (item)*〉
- font 是用一个name 和size 来描述的,它们都包含文本:
<!ELEMENT font (name, size)> <!ELEMENT name (#PCDATA) > <!ELEMENT size (#PCDATA) >
- 当一个元素可以包含文本时,那么就只有两种合法的情况:
- 只包含文本:
<!ELEMENT name (#PCDATA) >
- 包含任意顺序的文本和标签的组合:
<!ELEMENT para (#PCDATA|em|strong|code)*>
- 指定其他任何类型的包含# PCDATA 的规则都是不合法的:
<!ELEMENT captionedlmage (image, #PCDATA) >
- 只包含文本:
属性规则
描述合法的元素属性的规则,其通用语法为:
〈!ATTLIST element attribute type default>
示例:
<!ATILIST font style (plainlboldlital|bold-italic) "plain">
font 元素的style 属性:它有4 个合法的属性值,默认值是plain 。
<!ATILIST size unit CDATA #IMPLIED>
size 元素的unit 属性可以包含任意的字符数据序列
使用DTD
- 通知文档生成工厂打开验证特性:
factory.setVa1idating(true);
- 设置生成器忽略文本节点中的空白字符:
factory.setlgnoringElementContentWhitespace(true);
- (验证的最大好处是可以忽略元索内容中的空白字符)
- 当解析器报告错误时`应用程序希屯对该错误执行某些操作:
- 只要使用验证,就应该安装一个错误处理器,这需要提供一个实现了“ErrorHandler”接口的对象。这个接口有三个方法:
void warning(SAXParseException exception) void error(SAXParseException exception) void fatalError(SAXParseExcepti on exception)
- 可以通过“DocumentBuilder”类的“setErrorHandler”方法来安装错误处理器:
bui1der.setErrorHandler(handler);
相关方法
javax.xml.parsers.DocumentBuilder 1.4
- void setEntityResolver(EntityResolver resolver)
- 设置解析器,来定位要解析的XML 文档中引用的实体。
- void setError‘ Handler(ErrorHandler handler)
- 设置用来报告在解析过程中出现的错误和警告的处理器。
org.xml.sax.EntltyResolver 1.4
- public InputSource resolveEntity(String publicID, String systemID)
- 返回一个输入源,它包含了被给定D 所引用的数据,或者,当解析器不知道如何解析这个特定名字时,返回null 。如果没有提供公共D,那么参数publicID 可以为null 。
org.xml.sax.lnputSource 1.4
- InputSource( InputStream in)
- InputSource(Reader in)
- InputSource( String system ID)
- 从流、读人器或系统ID (通常是相对或绝对URL )中构建输入源。
org.xml.sax.ErrorHandler 1.4
- void fatalError(SAXParseException exception)
- void error( SAXParseExcept ion exception)
- void warning( SAXParseExcept ion exception)
- 覆盖这些方法以提供对致命错误、非致命错误和警告进行处理的处理器。
org.xml.sax.SAXParsaeException 1.4
- int getLineNumber()
- int getColumnNumber()
- 返回引起异常的已处理的输入信息末尾的行号和列号。
javax.xml.parses.DocumentBuilderFactory 1.4
- boolean isValidating()
- void setValidating(boolean value)
- 获取和设置工厂的validating 属性。当它设为true 时,该工厂生成的解析器会验证它们的输入信息。
- boolean isIgnoringElementContentWhitespace()
- void setlgnoringElemnentContentWhitespace(boolean value)
- 获取和设置工厂的ignoringElementContentWhitespace 属性。当它设为true时,该工厂生成的解析器会忽略不含混合内容( 即,元素与# PCDATA 混合)的元素节点之间的空白字符。
XML Schema
XML Schema规则
- 【XML Schema比DTD复杂得多,需要另外了解】
- 前缀“xsd:”(或“xs:”)来表示XSLSchema 定义的命名空间。
- 如果要在文档中引用Schema 文件,需要在根元素中添加属性:
<?xml version="l.0"?> <configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi: noNamespaceSchemalocation="config.xsd"> ... </configuration> 即:Schema 文件config.xsd 会被用来验证该文档。
- Schema 为每个元素都定义了类型:
- 类型可以是简单类型, 即有格式限制的字符串,或者是复杂类型。
- 一些简单类型巳经被内建到了XML Schema 内:
xsd:string xsd:int xsd:boo1ean
- 可以定义自己的简单类型:
如下,面是一个枚举类型: <Xsd:simpleType name= "StyleType"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="PLAIN" /> <xsd:enumeration value="BOLD" /> <xsd:enumeration value="ITALIC" /> <xsd:enumeration val ue="BOLD_ITALIC" /> </xsd:restriction> </xsd:simpleType>
- 当定义元素时,要指定它的类型,以约束了元素的内容:
<xsd:e1ement name="name" type="xsd:string"/> <xsd:element name="size" type="xsd:int"/> <Xsd:element name="style" type="StyleType"/>
- 可以把类型组合成复杂类型:
<xsd:complexType name="FontType"> <xsd:sequence> <xsd:element ref="name"/> <xsd:element ref="size"/> <xsd:element ref="style"/> </xsd:sequence> </xsd:complexType> 其中,“ref”属性来引用在Schema 中位千别处的定义,等同于嵌套定义: <Xsd:complexType name="FontType"> <xsd:sequence> <xsd:element name="name" type="xsd:string"/> <xsd:e1ement name="size" type="xsd:int"/> <xsd:element name="style" type="StyleType"> <xsd:simpleType> <xsd:restriction base="xsd:string"> <xsd:enumeration value="PLAIN" /> <xsd:enumeration value="BOLD" /> <xsd:enumeration value="ITALIC" /> <xsd:enumeration value="BOLD_ITALIC" /> </xsd:restriction> </xsd:simpleType> </xsd:e1ement> </xsd:sequence> </xsd:complexType>
- “xsd:sequence”结构和DTD 中的“连接符号”等价,而“xsd:choice”结构和“|”操作符等价:
<xsd:comp1exType name="contactinfo "> <xsd:choice> <xsd:element ref="email"/> <xsd:element ref="phone"/> </xsd:choice> </xsd:comp1exType> 和 DTD 中的类型“email|phone”类型是等价的。
- 如果要允许重复元素,可以使用“minoccurs”和“maxoccurs”属性:
<xsd:element name="item" type=" ... " minoccurs="O" maxoccurs="unbounded"> 与DTD 类型“item*”等价
- 如果要指定属性,可以把“xsd:attribute”元素添加到complexType 定义中去:
<xsd:element name="size"> <xsd:complexType> ... <xsd:attribute name="unit" type="xsd:string" use="optional" default="cm"/> </xsd:complexType> </xsd:element> 与下面的DTD 语句等价: <!ATTLIST size unit CDATA #IMPLIED "cm">
- 可以把Schema 的元素和类型定义封装在“xsd:schema”元索中:
<xsd:schema xmlns :xsd="http://www.w3.org/2001/XMLSchema"> ... </xsd: schema>
使用XML Schema
解析带有Schema 的XML 文件和解析带有DTD 的文件相似,但有3 点差别:
- 必须打开对命名空间的支待:
- (即使在XML 文件里你可能不会用到它)
factory.setNamespaceAware(true);
- 必须通过如下的“魔咒”来准备好处理Schema 的工厂:
final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
- 解析器不会丢弃元素中的空白字符;
- (有一种变通方法)【???】
生成XML文档
使用DOM
用文档的内容构建一棵DOM 树,然后再写出该树的所有内容。
构建:不带命名空间的XML
- 获取文档生成器工厂:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); builder = factory.newDocumentBuilder();
- 得到空文档:
Document doc= builder.newDocument();
- 构建文档元素:
- 使用“Document”类的“createElement”方法;
Element rootElement = doc.createElement(rootName); Element childElement = doc.createElement(childName);
- 构建文本节点:
- 使用“Document”类的“createTextNode”方法;
Text textNode = doc.createlextNode(textContents);
- 设置元素属性:
- 调用“Element”类的“setAttribute”方法;
rootElement.setAttribute(name, va1ue);
- 给文档添加根元素,给父结点添加子节点:
doc.appendChild(root Element); rootElement.appendChild(child Element); childElement.appendChild(textNode);
构建:带命名空间的XML
如果要使用命名空间.那么创建文档的过程就会稍微有些差异:
- 获取文档生成器工厂:(设置命名空间感知)
- 调用“DocumentBuilderFactory”对象的“setNamespaceAware”方法;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); builder = factory.newDocumentBuilder();
- 构建文档元素:(设置元素的命名空间)
- 使用“Document”类的“createElementNS”而不是“createElement”;
String namespace = "http://www.w3.org/2000/svg"; Element rootElement = doc.createElementNS(namespace, "svg");
- 如果节点具有带命名空间前缀的限定名,那么所有必需的带有xmlns 前缀的属性都会被自动创建。
// 例如,如果需要在HTML 中包含SVG, 那么就可以像下面这样构建元素: Element svgElement = doc.createElement(namespace, "svg:svg"); // 当该元素被写入XML 文件时,它会转变为: // <svg:svg xmlns:svg="http://www.w3.org/2000/svg">
- 设置元素属性:(设置属性的命名空间)
- 使用“Element”类的“setAttributeNS”方法:
rootElement.setAttributeNS(namespace, qualifiedName, va1ue);
写出文档
把DOM 树写出到输出流中并非一件易事:
XSLT
最容易的方式是:“可扩展的样式表语言转换”(Extensible Stylesheet Language Transformations, XSLT)API。【更多信息参见:#XSL_转换】
// construct the do-nothing transformation Transformer t = TransformerFactory.newlnstance().newTransformer(); // set output properties to get a DOCTYPE node t.setOutputProperty(OutputKeys.DOCTYPUYSTEM, systemIdentifier); t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicIdentifier); // set indentation t.setOutputProperty(OutputKeys.INDENT, "yes"); t.setOutputProperty(OutputKeys.METHOD, "xml"); t.setOutputProperty("{http://xml.apache.org/xs1t}indent-amount", "2"); // apply the do-nothing transformation and send the output to a file t.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(file)));
以上:
- 把“不做任何操作”的转换应用于文档,并且捕获它的输出;
- 为了将DOCTYPE 节点纳入输出,我们还需要将SYSTEM 和PUBLIC 标识符设置为输出属性;
LSSerializer
另一种方式是使用“LSSerializer”接口:
- 获取“LSSerializer”实例:
DOMImplementation impl = doc.getImplementation(); DOMImplementationLS implLS = (DOMImplementationLS) impl.getFeature("LS", "3.0"); LSSerializer ser = implLS.createLSSerializer(); // 如果需要空格和换行,设置标志: ser.getDomConfig().setParameter("format-pretty-print", true);
- 输出DOM:
- 输出到字符串:
String str = ser.writeToString(doc);
- 输出到文件:(需要一个“LSOutput”)
LSOutput out = implLS.createLSOutput(); out.setEncoding("UTF-8"); out.setByteStream(Fi1es.newOutputStream(path)); ser.write(doc, out);
- 输出到字符串:
相关方法
javax.xml.parsers.DocumentBuilder 1.4
- Document newDocument()
- 返回一个空文档。
org.w3c.dom.Document 1.4
- Element createElement(String name)
- Element createElementNS(String uri, String qname)
- 返回具有给定名字的元素。
- Text createTextNode(String data)
- 返回具有给定数据的文本节点。
org.w3c.dom.Node 1.4
- Node appendChild( Node child)
- 在该节点的子节点列表中追加一个节点。返同被追加的节点。
arg.w3c.dom.Element 1.4
- void setAttribute(String name, String value)
- void setAttri buteNS(String uri , String qname , String va 1 ue)
- 将有给定名字的屈性设甡为指定的值。
- 参数: uri 名字空间的URI 或null
- qname 限定名。如果有别名前缀, 则uri 不能为null
- value 属性值
javax.xml.transform.TransformerFactory 1.4
- static Trans formerFactory newlns tance()
- 返回Tran s formerFa c tory 类的一个实例。
- Transformer newTransformer( )
- 返回Transformer 类的一个实例,它实现了标识符转换(不做任何事情的转换) 。
javax.xml.transform. Transformer 1.4
- void setOutputProperty(String name, String value)
- 设置输出属性。标准输出属性参见http://www.w3.org/TR/xslt#output,其中最有用的几个如下所示:
- 参数: doctype- public DOCTYPE 声明中使用的公共ID
- doctype-system DOCTYPE 声明中使用的系统ID
- Indent "yes" 或者"no"
- method "xml"、"html"、"text" 或定制的字符串
- void transform (Source from, Result to)
- 转换一个XML 文档。
javax.xml.transform.dom.DOMSource 1.4
- DOMSource(Node n)
- 从给定的节点中构建一个源。通常,n 是文档节点。
javax.xml.transform.stream.StreamResult 1.4
- StreamResult(File f)
- StreamResult(OutputStream out)
- StreamResult(Writer out)
- StreamResult(String systemID)
- 从文件、流、写出器或系统TD( 通常是相对或绝对URL) 中构建流结果。
使用StAX 写出XML文档
StAX API 使我们可以直接将XML 树写出:
- 从某个“OutputStream”中构建一个“XMLStreamWriter”:
XMLOutputfactory factory = XMLOutputfactory.newInstance(); XMLStreamWriter writer = factory.createXMLStreamWriter(out);
- 写入内容:
- XML 文件开始
writer.writeStartDocument();
- 元素开始
writer.writeStartElement(name);
- 属性
writer.writeAttribute(name, value);
- 字符 或 子元素
writer.writeCharacters(text);
writer.writeStartElement(name); . . . writer.writeEndElement();
- 元素结尾
writer.writeEndElement();
- 无子节点元素(如“<img .../>”)
writer.writeEmptyElement(name);
- XML文件结尾
writer.writeEndDocument();
- 与使用DOM/XSLT 的方式一样,不必担心属性值和字符数据中的转义字符。
- 仍旧有可能会产生非良构的XML(例如具有多个根节点的文档)。
- StAX 没有任何对产生缩进输出的支持。
相关方法
javax.xml.stream.XMLOutputFactory 6
- static XMLOutputFactory newlnstance()
- 返回XMLOutputFactory 类的一个实例。
- XMLStreamWri ter createXMLStreamWri ter(OutputStream in)
- XMLStreamWri ter createXMLStreamWri ter(Output Stream in , String characterEncoding)
- XMLStreamWri ter createXMLStreamWri ter(Writer in)
- XMLStreamWriter createXMLStreamWriter(Result in)
- 创建写出到给定流、写出器或JAXP 结果的写出器。
javax.xml.stream.XMLStreamWriter 6
- void wri teStartDocument()
- void writeStartDocument(String xmlVersion)
- void writeStartDocument(String encoding , String xmlVersion)
- 在文档的顶部写入XML 处理指令。注意, encoding 参数只是用于写人这个屈性,它不会设置输出的字符编码机制。
- void setDefaul tNamespace(String namespaceURI )
- void setPrefix(String prefix, String namespaceURI)
- 设置默认的命名空间,或者具有前缀的命名空间。这种声明的作用域只是当前元素,如果没有写明具体元素,其作用域为文档的根。
- void writeStartElement(String local Name)
- void writeStartElement(String namespaceURI, String localName )
- 写出一个开始标签,其中namespaceURI 将用相关联的前缀来代替。
- void wri teEndEl ement()
- 关闭当前元素。
- void wri teEndDocument()
- 关闭所有打开的元素。
- void writeEmptyElement(String local Name)
- void writeEmptyEl ement(String namespaceURI, String 1 oca l Name)
- 写出一个自闭合的标签,其中namespaceURI 将用相关联的前缀来代替。
- void writeAttribute(String local Name, String value)
- void writeAttribute(String namespaceURI, String local Name, String value)
- 写出一个用于当前元素的属性,其中namespaceURI 将用相关联的前缀来代替。
- void writeCharacters (String text)
- 写出字符数据。
- void writeCData(String text)
- 写出CDATA 块。
- void writeDTD(String dtd)
- 写出dtd 字符串,该字串需要包含一个DOCTYPE 声明。
- void writeComment(String comment)
- 写出一个注释。
- void close()
- 关闭这个写出器。
示例
XSL 转换
XSL 转换( XSLT ) 机制可以指定将XML 文档转换为其他格式的规则,通常用于:
- 将某种机器可读的XML 格式转译为另一种机器可读的XML 格式
- 将XML 转译为适千人类阅读的表示格式
- 使用XSL需要提供“XSLT样式表”,它描述了XML 文档向某种其他格式转换的规则。
XSLT 处理器将读入XML文档和XSLT样式表,产生所要的输出:
XSLT处理器以检查根元素开始其处理过程:
- 每当一个节点匹配某个模板时, 就会应用该模板(如果匹配多个模板,就会使用最佳匹配的那个)。如果没有匹配的模板,处理器会执行默认操作。
- 对于文本节点, 默认操作是把它的内容囊括到输出中去。对于元素,默认操作是不产生任何输出,但会继续处理其子节点。
使用
在Java平台下产生XML的转换极其简单,只需为每个样式表设置一个转换器工厂,然后得到一个转换器对象,并告诉它把一个源转换成结果:
File styleSheet = new File(filename);
StreamSource styleSource = new StreamSource(styleSheet);
Transformer t = TransformerFactory.newInstance().newTransformer(styleSource);
t.transform(source,result);
“transform”方法的参数是“Source”和“Result”接口的实现类的对象。
- “Source”接口有4个实现类:
- DOMSource
- SAXSource
- StAXSource
- StreamSource
- 可以从一个文件、流、阅读器或URL中构建StreamSource对象,或者从DOM树节点中构建DOMSource对象。
- 将非XML的遗留数据转换成XML:
t.transform(new SAXSource(newEmployeeReader(), new InputSource(new FilelnputStream(filename))), result);
- 大多数XSLT应用程序都已经有了XML格式的输入数据,只需要在一个StreamSource对象上调用transform方法即可:
t.transform(newStrearnSource(file), result);
- “Result”接口有3个实现类:
- DOMResult
- SAXResult
- StreamResult
- 转换结果存储:
- 存储到DOM树中:
- 使用“DocumentBuilder”产生一个新的文档节点,并将其包装到“DOMResult”中;
Document doc= builder.newDocument(); t.transform(source, new DOMResult(doc));
- 保存到文件中:
- 使用“StreamResult”;
t.transform(source, new StreamResult(file));
- 存储到DOM树中:
相关方法
javax.xml.transform.TransfonnerFactory 1.4
- Transformer newTransformer(Source styleSheet)
- 返回一个transformer类的实例,用来从指定的源中读取样式表。
javax.xml.transform.stream.StreamSource 1.4
- StreamSource(File f)
- StreamSource(InputStream in)
- StreamSource(Reader in)
- StreamSource(String system ID)
- 自一个文件、流、阅读器或系统ID(通常是相对或绝对URL)构建一个数据流源。
javax.xml.transform.sax.SAXSource 1.4
- SAXSource (XMLReaderreader, InputSource source)
- 构建一个SAX数据源,以便从给定输入源中获取数据,并使用给定的阅读器来解析输入数据。
org.xml.sax.XMLReader 1.4
- void setContentHandler(ContentHandler handler)
- 设置在输入被解析时会被告知解析事件的处理器。
- void parse(InputSource DOMResult)
- 解析来自给定输入源的输入数据,并将解析事件发送到内容处理器。
javax.xml.transform.dom.SAXSource 1.4
- DOMResult(Node n) 自给定节点构建一个数据源。通常,n是一个新文档节点。
org.xml.sax.helpers.AttributesImpl 1.4
- void addAttribute(String uri, String lname, String qname, String type, String value)
- 将一个属性添加到该属性集合。
- 参数:uri 名字空间的URI回
- 1name 元前缀的本地名
- qname 带前缀的限定名
- type 类型,“CDATA”、“ID”、“IDREF”、“IDREFS”、“NMTOKEN”、“NMTOKENS”、“ENTITY”、“ENTITIES”或“NOTATION”之一
- value 属性值
- void clear()
- 删除属性集合中的所有属性。