1、概述
XML是一种非常有用的描述结构化信息的技术。XML工具使处理和转化信息变得十分容易。
- XML是大小写敏感的
- XML中结束标签绝对不能省略
- 在XML中,只有单个标签而没有相对应的结束标签的元素必须以/结尾
- 在XML中,属性值必须用引号括起来
- 在XML中,所有属性必须都有属性值
2、XML文档的结构
XML文档应当以一个文档头开始,例如
<?xml version="1.0"?>
或者
<?xml version="1.0" encoding="UTF-8"?>
文档头之后通常是文档类型定义(DTD),例如
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
文档类型定义是确保文档正确的一个重要机制,但是它不是必需的。
最后,XML文档的正文包括根元素,根元素包含其他元素。
元素可以有子元素、文本或两者皆有。
XML元素可以包含属性,例如
<size unit="pt">37</size>
元素和文本是XML文档"主要的支撑要素",还有其他的可能遇到的标记,说明如下:
- 字符引用
- 实体引用:形式是&name;。下面这些实体引用:< > & " &apos都有预定义的含义:小于、大于、&、引号、省略号等字符。还可以在DTD中定义其他的实体引用。
- CDATA部分:用<!CDATA[ 和 ]]>来限定其界限。可以使用它们来囊括哪些含有<、>、&之类字符的字符串,而不必将它们解释为标记。
- 处理指令
- 注释:用<!-- 和 -->限定其界限。
3、解析XML文档
Java库提供两种XML解析器:
- 像文档对象模型(DOM)解析器这样的树形解析器,它们将读入的XML文档转换成树结构。
- 像XML简单API(SAX)解析器这样的流机制解析器,它们在读入XML文档时生成响应的事件。
DOM解析器的接口已经被W3C标准化了。org.w3c.dom包中包含了这些接口类型的定义,比如:Document和Element等。
要读入一个XML文档,首先需要一个DocumentBuilder对象,可以从DocumentBuilderFactory中得到这个对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
现在,可以从文件或者URL或者输入流读入某个文档
File | URL | InputStream f = ...;
Document doc = builder.parse(f);
Document对象是XML文档的树型结构在内存中的表示方式,它由实现了Node接口及其各种子接口的类的对象构成。
可以通过调用getDocumentElement方法来启动对文档内容的分析,它将返回根元素。
Element root = doc.getDocumentElement();
例如,如果要处理下面的文档
<?xml version="1.0">
<font>
...
</font>
那么,调用getDocumentElement方法可以返回font元素。getTagName方法可以返回元素的标签名。在前面这个例子中,root,getTagName()返回字符串"font"。
如果要得到该元素的子元素,使用getChildNodes方法,这个方法会返回一个类型为NodeList的集合。item方法将得到指定索引值的项;getLength方法则提供了项的总数。因此,我们可以像下面这样枚举所有子元素:
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
{
Node child = children.item(i);
...
}
假如你正在处理以下文档:
<font>
<name>Helvetica</name>
<size>36</size>
</font>
在分析name和size元素时,你肯定想获取它们包含的文本字符串。这些字符串本身都包含在Text类型的子节点中。既然知道了这些Text节点是唯一的子元素,就可以使用getFirstChild方法而不用再遍历另一个NodeList。然后可以用getData方法获取存储在Text节点中的字符串。
for( int i = 0; i < children.getLength(); i++)
{
Node child = children.item(i);
if (child instanceof Element)
{
Element childElement = (Element) child;
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得到下一个兄弟节点。
如果要枚举节点的属性,可以调用getAttributes方法。它返回一个NamedNodeMap对象,其中包含了描述属性的Node对象。可以用和遍历NodeList一样的方式在NamedNodeMap中遍历各子节点。然后,调用getNodeName和getNodeValue方法可以得到属性名和属性值。
NamedNodeMap attributes = element.getAttributes();
for (int i= 0; i < attributes.getLength(); i++)
{
Node attribute = attributes.item(i);
String name = attribute.getNodeName();
String value = attribute.getNodeValue();
...
}
或者,如果知道属性名,则可以直接获取相应的属性值
String unit = element.getAttribute("unit");
4、验证XML文档
在前面我们了解了如何遍历DOM文档的树型结构。然而,如果仅仅按照这种方法来操作,会发现需要大量冗长的编程和错误检测工作。不但需要处理元素间的空白字符,还要检查该文档包含的节点是否和期望的一样。
幸好,XML解析器的一个很大的好处就是它能自动校验某个文档是否具有正确的结构。
如果要指定文档结构,可以提供一个文档类型定义(DTD)或一个XMl Schema定义i。DTD或schema包含了用于解释文档应如何构成阿规则,这些规则指定了每个元素的合法子元素和属性。例如,某个DTD可能含有一项规则:
<!ELEMENT font (name,size)>
这项规则表示,一个font元素必须总是有两个子元素,分别是name和size。将同样的约束用XML Schema表示如下:
<xsd:element name="font">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="size" type="xsd:int“/>
</xsd:sequence>
</xsd:element>
与DTD相比,XML Schema可以表达更加复杂的验证条件。
4.1文档类型定义
提供DTD的方式有多种。可以像下面这样将其纳入到XML文档中:
<?xml version="1.0"?>
<!DOCTYPE config [
<!ELEMENT config ...>
more rules
...
]>
<config>
...
</config>
这些规则被纳入到DOCTYPE声明中,位于由[…]限定的块中。文档类型必须匹配根元素的名字。
在XML文档内部提供DTD不是很普遍,因为DTD会使文件长度变得很长。把DTD存储在外部会更具意义,SYSTEM声明可以用来实现这个目标。可以指定一个包含DTD的URL。例如;
<!DOCTYPE confiig SYSTEM ”confiig.dtd">
最后,有一个来源于SGML的用于识别"众所周知的"DTD的机制,下面是一个例子:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
ELEMENT规则用于指定某个元素可以拥有什么样的子元素。可以指定一个正则表达式。
用于元素内容的规则
规则 | 含义 |
---|---|
E* | 0或多个E |
E+ | 1或多个E |
E? | 0或1个E |
E1|E2|…|En | E1,E2,…En中的一个 |
E1,E2,…,En | E1后面跟着E2,…,En |
#PCDATA | 文本 |
(#PCDATA|E1|E2|…|En)* | 0或多个文本且E1,E2,…,En以任意顺序排列(混合式内容) |
ANY | 允许有任意子元素 |
EMPTY | 不允许有子元素 |
还可以指定描述合法的元素属性的规则,其通用语法为:
<!ATTLIST element attribute type default>
属性类型
类型 | 含义 |
---|---|
CDATA | 任意字符串 |
(A1|A2|…|An) | 字符串属性A1A2…|An之一 |
NMTOKEN,NMTOKENS | 1或多个名字标记 |
ID | 1个唯一的ID |
IDREF,IDREFS | 1或多个对唯一ID的引用 |
ENTITY,ENTITIES | 1或多个未解析的实体 |
属性的默认值
默认值 | 含义 |
---|---|
#REQUIRED | 属性是必需的 |
#IMPLIED | 属性是可选的 |
A | 属性是可选的;若未指定,解析器报告的属性是A |
#FIXED A | 属性必须是未指定的或者是A;在这两种情况下,解析器报告的属性都是A |
以下是两个典型的属性规格说明:
<!ATTLIST font style (plain|bold|italic|bold-italic) "plain">
<!ATTLIST size unit CDATA #IMPLIED>
第一个规格说明描述了font元素的style属性。它有4个合法的属性值,默认值是plain。第二个规格说明表示size元素的unit属性可以包含任意的字符数据序列。
4.1、配置DTD解析器
首先,通知文档生成工厂打开验证特性。
factory.setValidating(true);
忽略空白字符:
factory.setIgnoringElementContentWhitespace(true);
安装错误处理器,提供一个实现了ErrorHandler接口的对象:
builder.setErrorHandler(handler);
4.2、XML Schema
如果要在文档中引用Schema文件,需要在根元素中添加属性,例如:
<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceSchemaLocation="config.xsd">
...
</config>
这个声明说明了Schema文件config.xsd会被用来验证该文档。
Scanme为每个元素和属性都定义了类型。类型中的简单类型是对内容有限制的字符串,其他都是复杂类型。具有简单类型的元素可以没有任何属性和子元素。否则,它就必然是复杂类型。与此相反,属性总是简单类型。
一些简单类型已经被内建到了XML Schema内,包括:
xsd:string
xsd:int
xsd:boolean
可以定义自己的简单类型。例如,下面是一个枚举类型:
<xsd:simpleType name="StyleType">
<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
当定义元素时,要指定它的类型:
<sd:element name="name" type="xsd:string"/>
<sd:element name="size" type="xsd:int"/>
<sd: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>
FontType是name、size和style元素的序列。在这个类型定义中,我们使用了ref属性来引用在Schema中位于别处的定义。
Xsd:sequence结构和DTD中的连接符号等价,而xsd:choice结构和 | 操作符等价。
如果要允许重复元素,可以使用minoccurs和maxoccurs属性,例如,与DTD类型item*等价的形式如下:
<xsd:element name="item" type="..." minoccurs="0" maxoccurs="unbounded">
如果要指定属性,可以把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的XML文件和解析带有DTD的文件相似,但有2点差别:
- 必须打开对命名空间的支持,即使在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);
5、使用XPath来定位信息
6、使用命名空间
名字空间是由统一资源标识符(URI)来标识的,比如
http://www.w3.org/2001/XMLSchema
urn:com:books-r-us
HTTP的URL格式是最常见的标识符。
在Java编程语言中,可以用import机制来指定很长的包名,然后就可以使用较短的类名了。在XML中有类似的机制,比如:
<element xmlns="namespaceURI">
children
</element>
现在,该元素和它的子元素都是给定命名空间的一部分了。
第二种机制是可以用一个前缀来表示命名空间,即为特定文档选取的一个短的标识符。下面是一个典型的例子:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="config"/>
...
</xsd:schema>
下面的属性
xmlns:prefix="namespaceURI"
用于定义命名空间和前缀。在我们的例子里前缀是字符串xsd。这样,xsd:schema实际上指的是命名空间http://www.w3.org/2001/XMLSchema中的schema。
每个节点有三个属性:
- 带有前缀的限定名,由getNodeName和getTagName等方法返回。
- 命名空间URI,由getNamespaceURI方法返回。
- 不带前缀和命名空间的本地名,由getLocalName方法返回。
下面是一个例子。假设解析器看到了以下元素:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
它会报告如下信息:
- 限定名 = xsd:schema
- 命名空间URI = http://www.w3.org/2001/XMLSchema
- 本地名 = schema
7、流机制解析器
SAX解析器使用的是事件回调,而StAX解析器提供了遍历解析事件的迭代器。
…
8、生成XML文档
…
8.1、不带命名空间的文档
要建立一颗DOM树,可以从一个空的文档开始。通过调用DocumentBuilder类的newDocument方法可以得到一个空文档。
Document doc = builder.newDocument();
使用Document类的createElement方法可以构建文档里的元素:
Element rootElement = doc.createElement(rootName);
Element childElement = doc.createElement(childName);
使用createTextNode方法可以构建文本节点:
Text textNode = doc.createTextNode(textContents);
使用以下方法可以给文档添加根元素,给父节点添加子节点:
doc.appendChild(rootElement);
rootElement.appendChild(childElement);
childElement.appendChild(textNode);
8.2、带命名空间的文档
首先需要将生成器工厂设置为是命名空间感知的,然后创建生成器:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
再使用createElementNS而不是createElement来创建所有节点:
String namespace = "http://www.w3.org/2000/svg";
Element rootElement = doc.createElementNS(namespace,"svg");
8.3、写出文档
…
9、XSL转换
XSL转换(XSLT)机制可以指定将XML文档转换成其他格式的规则。
需要提供XSLT样式表,它描述了XML文档向某种其他格式转换的规则。
XSLT处理器以检查根元素开始其处理过程。每当一个节点匹配某个模板时,就会应用该模板。如果没有匹配的模板,处理器会执行默认操作。对于文本节点,默认操作是把它的内容囊括到输出中去。对于元素,默认操作是不产生任何输出,但会继续处理其子节点。
…