第二章 XML

2.1 XML 概述

属性文件是一种单一的平面层次结构,并且要求键值是唯一的。
XML能够表示层次,解决了上述问题。
XML与HTML都是标准通用标记语言(SGML)的衍生

XML和HTML的重要区别:
  • XML是大小写敏感的,例如<H1><h1>是不同的XML标签
  • 在HTML中,如果从上下文可以分清哪里是段落或列表项的结尾,
    那么结束标签就可以省略,而在XML中结束标签绝对不能省略。
  • XML中,只有单个标签而没有相对应的结束标签的元素必须以/结尾,
    比如<img src="coffeecup.png"/> 这样,解析器就知道不需要查找</img>标签
  • XML中,属性值必须用引号括起来。
  • 在HTML中,属性名可以没有值。
文档的结构

XML文档应当以一个文档头开始

<?xml version="1.0" encoding="UTF-8"?>

因为建立SGML是为了处理真正的文档,因此XML文件叫做文档。
尽管许多XML文件是用来描述通常不称作文档的数据集的。

●文档头之后通常是文档类型定义(Document Type Definition,DTD
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
文档类型定义的是确保文档正确的一个重要机制,但同文档头一样也不是必需的。

XML文档正文包含根元素,根元素包含其他元素
元素可以有子元素或者文本。设计时应该避免混合式内容

<font>
    Helvetica
    <size>36</size>
</font>

XML文档包含属性,属性只应该用来修改值得解释,而不是用来指定值。
在HTML中属性的使用规则很简单:凡是不显示在网页上的都是属性。

一些标记:

  • 字符引用(&#十进制值,&#x16进制值)
    &#233,&#xE9
  • 实体引用(&name)
    &lt;&gt;&amp;&quot;&apos;
  • CDATA部分,用<![CDATA[]]>来界定其界限,是字符数据的一种特殊形式。
    可以用来显示包括<,>,&之类的字符的字符串
    <![CDATA[<&> are my favorite delimiters]]>
    不能包含字符串]]
  • 处理指令(processing instruction)用<?和?>来限定其界限。
    <?xml-stylesheet href="mystyle.css" type="text/css"?>
  • 注释(comment)用<!--和-->来限定其界限。
    不能包含字符串 --,也绝不应该包含隐藏的命令。

2.2 解析XML文档

用解析器解析XML文档。

  1. 读入一个文件
  2. 确认这个文件具有正确的格式
  3. 将其分携程各种元素。
  4. 提供访问这些程序的接口

两种解析器:

  • 树形解析器,将读入的XML文档转换成树结构。
    例如:文档对象模型(Document Object Model, DOM)解析器,
    实现简单,但生成的树结构将会消耗大量内存。
  • 流机制解析器,在读入XML文档时生成相应的事件。
    XML简单API(Simple API for XML, SAX)解析器
    不关心上下文←这句话怎额理解

DOM解析器的接口已经标准化了,Apache Organization和IBM都编写了这些接口的解析器。
JavaXML处理APIJava API for XML Processing,JAXP)库
使得实际上可以以插件形式使用这些解析器中的任意一个。

另外JDK中也包含了一个自己的DOM解析器

注意:如果使用输入流作为输入源,那么对于那些以本文档的位置作为相对路径而被引用的文档,
解析器将无法定位,比如在同一目录中的DTD。但是可以通过安装一个“实体解析器”来解决问题。

<font>
    <name>Helvetica</name>
    <size>36</size>
</font>
NodeList children = root.getChildNodes();
for (int i=0;i<children.getLength();i++) {
    Node Child = children.iten(i);
}

预期取回来两个子元素,但解析器报告却说有5个,另外3个分别为,
<font>和<name></name>和<size></size>和</font>之间的空白。

如果只希望得到子元素,则可以忽略空白。

NodeList children = root.getChildNodes();
for (int i=0;i<children.getLength();i++) {
    Node Child = children.iten(i);
    if (child instanceof Element){
        Element childElement = (Element) child;
        ...
    }
}

如果文档有DTD就可以做的更好。解析器知道哪些元素没有文本节点的子元素,
而且会帮你剔除空白字符。

NodeList children = root.getChildNodes();
for (int i=0;i<children.getLength();i++) {
    Node Child = children.iten(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;
        }
        if (childElement.getTagName().equals("size")){
            name = Integer.parseInt(text);
        }
    }
}

对getData返回值调用trim是个好主意,因为标签在不同行的时候,换行符和空格都在文本里。
对第二层元素,如果要遍历,同样还得用NodeList来循环,
上例因为知道下面已经没有子元素了。所以直接用FirstChild方法取得内容。

如果要枚举节点的属性,可以调用getAttributes方法,它返回一个NameNodeMap对象。
同样遍历NamedNodeMap来遍历子节点,通过getNodeName和getNodeValue来获取属性名和值

NameNodeMap attributes = element.getAttributes();
for(int i=0; i< attributes.getLength(); i++){
    Node attribute = attributes.item();
    String name = attribute.getNodeName();
    String value = attribute.getNodeValue();
}
//如果知道属性名,想取属性值
element.getAttribute("unit");

2.3 验证XML文档

上面是通过程序,验证文档的结构,以及进行错误的检查,程序非常冗长。
XML解析器可以自动校验某个文档是否具有正确的结构。

指定文档结构,提供一个文档类型定义(DTD)或一个XML Schema定义。 其包含了用于解释文档应如何构成的规则,这些规则指定了每个元素的合法子元素和属性。

例:DTD含有一个规则,font元素必须总是有两个子元素,name和size <!ELEMENT font(name,size)>

同样的XML Schema约束表示如下:

<xsd:element name = "font">
    <xsd:sequence>
        <xsd:element name = "name" type = "xsd:string"/>
        <xsd:element name = "font" type = "xsd:int"/>
    </xsd:sequence>
</xsd:element>

DTD相比,XML Schema可以表达更加复杂的验证条件。
DTD语法不同,Schema使用XML,这为处理Schema文件带来了方便。

XML Schema虽然被设计替代DTD,但比较复杂,所以现在还没有得到普遍采纳。

2.3.1 文档定义类型

提供DTD的方式有多种。可以像下面这样将其纳入XML文档中。

<?xml version="1.0"?>
<!DOCTYPE configuration [
    <!ELEMENT configuration ... >
    more rules
    ...
]>
<configuration>
...
</configuration>

规则被纳入到了DOCTYPE声明中,代码块使用了[]来限定其界限。
文档类型必须匹配根元素的名字

DTD存储在外面,然后在XML文档中引用比较常见。

<!DOCTYPE configuration SYSTEM "config.dtd">
<!DOCTYPE configuration SYSTEM "http://myserver.com/config.dtd">

这里的SYSTEM 有什么作用?

如果使用的DTD是相对Url,name要给解析器一个File或者URL对象,而不是InputStream。
如果必须从一个输入流来解释,需要一个实体解析器

来源于SGML的,用于识别“众所周知”DTD的机制

<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems,Inc.//DTD Web Application 2.2//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

如果XML处理器知道如何定位带有公共标识符的DTD,那么就不需要URL了。

指定描述合法的元素属性的规则。通用语法:
<!ATTLIST element attribute type default> 例如: <!ATTLIST font style (plain|bold|italic|bold-italic) "plain">
<!ATTLIST size unit CDATA #IMPLIED>

2.3.2 XML Schema

如果要在文档中引用Schema文件,需要在根元素中添加属性,
例如

<?xml version ="1.0"?>
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsl:noNamespaceSchemaLocation="config.xsd">
...
</configuration>

这个声明说明Schema文件config.xsd会被用来验证文档。
如果使用命名空间,语法就更加复杂了。详细请参见XML Schema指南
前缀xsi是一个命名空间别名(namespace alias)

2.3.3 实用实例

2.4 使用XPath来定位信息

2.5 使用命名空间

2.6 使用流机制解析器

2.6.1 使用SAX解析器
2.6.2 使用StAX解析

2.7 生成XML文档

2.7.1 不带命名空间的文档
2.7.2 带命名空间的文档
2.7.3 写出文档
2.7.4 示例生成SVG文件
2.7.5 使用StAxXML文档

2.8 XSL转换