“核心技术Ⅱ:XML”的版本间差异
(→使用命名空间) |
|||
第345行: | 第345行: | ||
=== | === 使用“XPath” === | ||
XPath 语言使得访问树节点变得很容易,而不必遍历DOM树。<br/> | XPath 语言使得访问树节点变得很容易,而不必遍历DOM树。<br/> | ||
2020年11月28日 (六) 05:46的版本
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 之间。
流机制解析器
使用“SAX”解析器
使用“StAX”解析器
使用“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 对象来计算多个表达式)
- 如果返回一个字符串,不用指定“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);
使用命名空间
验证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比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>
解析带有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);
- 解析器不会丢弃元素中的空白字符;
- (有一种变通方法)【???】