简介
人们都很喜欢 XML 以及它所提供的灵活性和互操作性,但是,通过使用一些技巧,可以使与 XML 的互操作和与 XML 一起使用的工具更加简单。在处理 XML 时养成一些良好的习惯可以保证最高效地利用您的 XML 文档和应用程序。
使用 10 种良好的习惯
以下是 10 种最良好的 XML 习惯:
- 定义 XML 和编码
- 使用 DTD 或 XSD
- 记得进行验证
- 验证并不总是能够解决问题
- XML 结构和属性
- 使用 XPath 查找信息
- 并不总是需要使用解析器提取信息
- 何时使用 SAX 而非 DOM 解析
- 何时使用 DOM 而非 SAX 解析
- 使用良好的 XML 编辑器
定义 XML 和编码
|
经常使用的缩写词
- DOM:文档对象模型(Document Object Model)
- DTD:文档类型定义(Document Type Definition)
- HTML:超文本标记语言(Hypertext Markup Language)
- IDE:集成开发环境(Integrated Development Environment)
- SAX:XML 简单 API(Simple API for XML)
- XSD:XML 模式定义(XML Schema Definition)
- XML:可扩展标记语言(Extensible Markup Language)
- XSLT:可扩展样式表语言转换(Extensible Stylesheet Language Transformations)
|
|
在快速创建 XML 文档时,一般都会倾向于创建基本的结构并避开一些普通 XML 文档需求,包括指定 XML 文档声明和 XML 文档包含的数据的编码类型。
考虑清单 1 所示的 XML 文档。
清单 1. 未包含 XML 声明和数据编码类型的 XML 文档
<phrases>
<phrase lang="en">Hello</phrase>
<phrase lang="it">Buongiorno</phrase>
<phrase lang="fr">Salut!</phrase>
</phrases>
|
对于普通人来说,可以查看该文档并将其识别为 XML,但是对于计算机来说,则很难作出这样的判断。在文件顶部添加 XML 声明,可以使它更加明确、更容易识别。一行简单的代码就可以说明文档是 XML,并且指出版本号和 XML 数据使用的字符编码类型。例如:
<?xml version="1.0" encoding="us-ascii"?>
|
编码说明中的内容也应该确保正确性。XML 解析器使用编码确保 XML 文档的单个字符被正确载入。例如,继续 清单 1 中基于短语的示例,如果向文档添加一个俄语条目,则会出现问题,因为目前指定的编码不支持扩展的字符集(使用俄语短语表示 hello 时要求使用扩展字符集)。
指定错误的编码意味着解析器不能正确处理文档;例如,如果将一个多字节扩展字符读取为一个单字节组成的序列,那么会导致数据的损坏和不良输出。
使用 DTD 或 XSD
添加了 XML 声明后,应该确保使用 DTD 或 XSD 定义有效的 XML 文件的结构。这两种方法都允许 XML 解析器检查并确定 XML 文件的内容与建模数据对应的结构相匹配。
例如,给出一个针对联系(contact)数据库的简单 XML 结构,您希望定义一种结构来指定联系人的姓名、地址和电话号码。使用 DTD 方法可以使您映射这种结构并确保结构中的每一个联系人与布局相匹配。
例如,清单 2 中显示了针对联系数据库的 DTD。
清单 2. 用于联系数据库的 DTD
<!ELEMENT phone (#PCDATA)>
<!ATTLIST phone type (home | work | mobile) #REQUIRED>
<!ELEMENT contact (#PCDATA | name | phone | address)*>
<!ELEMENT contacts (#PCDATA | contact)*>
<!ELEMENT country (#PCDATA)>
<!ELEMENT road (#PCDATA)>
<!ELEMENT address (#PCDATA | road | city | state | postcode | country)*>
<!ATTLIST address type (home | work) #REQUIRED>
<!ELEMENT state (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT postcode (#PCDATA)>
<!ELEMENT city (#PCDATA)>
|
DTD 定义了描述联系人所需要的元素、属性(以及这些属性所支持的值)。例如,在 清单 2 可以看到,phone 元素有一个 type 属性,而 address 和其中的 component 元素也有属性。
使用 DTD 可以帮助确保属性的有效性,并且,和验证过程一起使用时可以识别任何问题。当和支持 XML 的编辑器一起使用时,DTD 可以帮助编辑和自动完成内容。
XSDs,即模式,可以执行很多与 DTD 相同的功能,但有其独特的用途。例如,一些 XML 编辑器需要使用 DTD 自动完成内容,然而模式在设计文档实际层次结构方面更加灵活。您可以根据具体环境选择工具。
记得进行验证
查看清单 3,能否找出其中的问题?
清单 3. 验证示例
<contacts>
<contact>
<name>Martin</name>
<phone type="home">123 456 7890</phone>
<phone type="mobile">123 456 7890</phone>
<phone type="work">123 456 7890</phone>
<address type="home">
<road>Home road</road>
<city>Home city</city>
<state>Home state</state>
<zipcode>12434</zipcode>
<country>USA</country>
</address>
</contact>
<contact>
<name>Sharon</name>
<phone type="work">234 567 8901</phone>
<phone>234 567 8901</phone>
<address type="home">
<road>Other home road</road>
<city>Other city</city>
<state>Other state</state>
<zipcode>39487</zipcode>
<country>USA</country>
</address>
<address type="work>
<road>Work building, work road</road>
<city>Work city</city>
<state>Work state</state>
<zipcode>12347</zipcode>
<country>USA</country>
</address>
</contact>
</contacts>
|
手动查找问题非常麻烦。但是可以通过 xmllint(一款可以检验 XML 文件的内容和结构的免费工具)运行文件,您可以查看运行该文件的输出,如清单 4 所示。
清单 4. 通过 xmllint 运行清单 3 得到的输出
$ xmllint contacts.xml
contacts.xml:27: parser error : Unescaped '<' not allowed in attributes values
<road>Work building, work road</road>
^
contacts.xml:27: parser error : attributes construct error
<road>Work building, work road</road>
^
contacts.xml:27: parser error : Couldn't find end of Start Tag address line 26
<road>Work building, work road</road>
^
contacts.xml:32: parser error : Opening and ending tag mismatch: contact line 15
and address
</address>
^
contacts.xml:33: parser error : Opening and ending tag mismatch: contacts line 1
and contact
</contact>
^
contacts.xml:34: parser error : Extra content at the end of the document
</contacts>
|
尽管与最初的问题(其中的一个属性没有结束)相比似乎复杂了很多,但却为您查找问题提供了一个起点。
xmllint 还支持各种各样的命令行选项,帮助选择诊断方法和结果。其中最有用的一个选项是 --noout
,它阻止 xmllint 在解析文件时回传内容。对于较短的文件来说没有什么影响,但对于大型文件来说则是一个问题。
如果正在使用 DTD,那么使用 --postvalid
选项告诉 xmllint 针对 DTD 验证内容,确保内容不仅是有效的 XML,而且还与 DTD 的结构相匹配。如果将 使用 DTD 或 XSD 为 contact 文件生成的 DTD 添加到文件,那么属性定义错误将被纠正,但随后将生成一个不同的错误,如清单 5 所示。
清单 5. xmllint 找到不同的错误
$ xmllint --noout --postvalid contacts.xml
contacts.xml:9: element address: validity error : Element zipcode is not declared
in address list of possible children
contacts.xml:21: element address: validity error : Element zipcode is not declared
in address list of possible children
contacts.xml:28: element address: validity error : Element zipcode is not declared
in address list of possible children
Document contacts.xml does not validate
|
这样使用 xmllint 可以方便快捷地确定文档的结构是否有效。xmllint 是 libxml2 工具箱的一部分,该工具箱已绑定到 Linux、UNIX® 和 Mac OS X,但 Windows® 需要独立下载。有关 xmllint 和 libxml2 的更多信息,请参见 参考资料。
验证并不总是能够解决问题
使用 xmllint 和类似工具验证 XML 文件(特别是如果使用了 DTD),是验证 XML 文件内容的很好方法。然而,这种方法也有其局限性。例如,如何处理 XML 文件的内容?
使用 DTD 或 XSD,您可以为属性指定明确的内容。您只是创建了带有一个字符串或 ID 的属性(可以是受限制的可用选项列表的一部分),但是不能使用这种方式控制或限制元素的内容。
例如,在联系人示例中,telephone numbers 元素包含数字和空格。但是没办法阻止用户向该元素添加字母字符。这样做在使用 xmllint 进行验证时不会检查出错误,并且编辑器和其他支持 XML 的解决方案也无法解决或识别这个问题。应用程序出现的故障可能和您预料的一样,因为它识别出一个非标准数据类型。
简而言之,XML 验证只能保证结构正确,而无法保证数据的有效性。
解决此问题的最简单方法是编写一个解析器,它可以读取 XML 文件并实际验证数据内容。但是不要过度地验证内容,只需确保数据符合应用程序的要求。
XML 结构和属性
对于究竟是使用属性还是元素来描述希望在 XML 文件中呈现的信息,人们存在着不同的看法。
一般的做法是,使用元素(即标记之间的数据)定义文件包含的信息,而属性则用于提供所描述数据的扩展限制。
元素和属性都各有弊端。例如,属性不能够在标记中重复,这是元素优于属性的典型例子。元素支持重复信息的能力使其非常实用。相反,使用元素限制数据有时处理起来会比较复杂。
联系人示例中的电话号码很好地解释了属性的优点。在这个示例中,如清单 6 所示,使用属性限制电话号码的类型(例如办公、住宅或移动电话)。
清单 6. 限制电话号码的类型
<phone type="home">123 456 7890</phone>
<phone type="mobile">123 456 7890</phone>
<phone type="work">123 456 7890</phone>
|
使用这种结构,能够轻松地把号码作为一个整体(忽略属性),或者挑选特定的电话号码类型(使用属性)。
将此结构与清单 7 中只使用元素设计的结构进行比较。
清单 7. 只使用元素限制电话号码
<phone>
<type>home</type>
<number>123 456 7890</number>
</phone>
<phone>
<type>mobile</type>
<number>123 456 7890</number>
</phone>
<phone>
<type>work</type>
<number>123 456 7890</number>
</phone>
|
现在还很难判断孰优孰劣。尽管从理论上说任何 XML 解析器或适当的 XPath 定义都可以把您需要的信息抽取出来。但这样做获益不大,并且使得 XML 文档很难阅读。
使用 XPath 查找信息
在处理 XML 数据时,查找需要的信息非常复杂。您可以编写一个解析器来挑选需要的信息,但在某些情况下,您只需要快速地找到文件中的一小段信息。
例如,如果需要从联系人 XML 文件中提出所有国家的列表,以便查看联系人在全球的分布范围,那么可以使用 XPath 来挑选信息。
通过将 XML 文件的结构作为查询的一部分,XPath 使您能够从 XML 文件中抽取数据。例如,通过提供特定元素在 XML 文件中的路径,您可以提取该元素的数据:
$ xpath contacts.xml '//contact/address/country'
|
您可以按照下面这样分析内容:
- 最开头的双斜杠(//)表示在文档的任意位置查找指定的元素(contact)。
- 下一个斜杠和元素名指定了要查找的下一个元素(address)— 就是说,在 contact 元素内查找 address 元素。
- 最后的斜杠重复此过程,这一次查找的是 country 元素。
注意,在这个示例中,您限定了从中选择信息的地址的类型,因此将选择所有地址。您可以在清单 8 中查看 XPath 查询的结果。
清单 8. XPath 查询的结果
$ xpath contacts.xml '//contact/address/country'
Found 3 nodes:
-- NODE --
<country>USA</country>-- NODE --
<country>USA</country>-- NODE --
<country>USA</country>
|
如果需要挑选更具体的数据,可以指定要匹配的元素或属性的内容。例如,如果只选择手机号码,您需要指定属性类型和值。为此,使用(@)符号,它表示您要求搜索一个属性,然后指定需要匹配的值(参加清单 9 )。
清单 9. 只选择手机号码
$ xpath contacts.xml '//contact/phone[@type="mobile"]'
Found 1 nodes:
-- NODE --
<phone type="mobile">123 456 7890</phone>
|
清单 8 和 9 都使用了一个命令行工具。很多 XML 工具箱都提供了原生方法来处理 XPath 元素,并且您可以使用 XPath 规范提取数据在应用程序中直接使用,而不需要使用解析器来获取信息。
并不总是需要使用解析器提取信息
尽管有些意想不到,但您并一定需要使用一种功能完善的 XML 解析器,使用 SAX、DOM 或其他技术(如 XPath 或 XQuery)从 XML 文件中提取需要的信息。
XML 文件使用结构化的格式包含数据,但是有时您需要信息使用自身的结构化格式。要快速查找一个信息片段时,通常可以使用更简单的解决方法。
通常,您仅需使用 grep、Perl 或其他类似工具提取所需的数据,而不需要以 XML 文件的形式实际解释文档的结构或内容。
例如,您可以使用 grep 选择电话号码(参见清单 10)。
清单 10. 使用 grep 选择电话号码
$ grep '<phone' contacts.xml
<phone type="home">123 456 7890</phone>
<phone type="mobile">123 456 7890</phone>
<phone type="work">123 456 7890</phone>
<phone type="work">234 567 8901</phone>
<phone>234 567 8901</phone>
|
您使用 grep 选择了需要的信息,并且不需要考虑信息是 XML 格式或者信息的结构。
如果需要查找简短的信息片段,简化的处理技术可以查找到所需的信息,并且避免了使用传统解析方法的开销。
何时使用 SAX 而非 DOM 解析
当为文档构建一个解析器以获得所需信息时,常常很难决定何时使用基于 SAX 的处理程序,何时使用基于 DOM 的处理程序。
关于这个问题的最简单解决方法是同时考虑文档的复杂性和所查找信息的用途。如果要转换文档,或者文档非常大,那么 SAX 是最佳选择。
SAX 逐个解析文档元素,在识别元素时调用方法或函数。如果将一个 XML 文档转换为另一种格式,例如将 XML 转换成 HTML,那么 SAX 是最有效的方式。您不必将整个文档加入到内存中,只需响应被识别出的元素和结构。
SAX 的缺点是,如果需要保存或记录结构,或者理解整个文档并从其中挑选单个元素(例如,从所有记录中选择单个联系人),则必须构建复杂的处理程序,以加载数据并将数据记录到结构中,然后将元素标识到输出目标中。
何时使用 DOM 而非 SAX 解析
DOM 可以将整个文档及其结构载入到内存,并允许您在应用程序内部引用和使用 XML 文档的结构。如在联系人示例中,您可以将整个联系人数据库读入内存,然后通过遍历联系人选择所有的电话号码,接着在每个联系人内部遍历每个电话号码。
由于 DOM 保留了结构,更重要的是可以理解和处理结构,您可以轻松地对结构进行整体或单独的处理。仍然以联系人示例为例,使用 SAX 插入新的联系人将非常复杂。但是如果使用 DOM,您只需将一个表示新联系人的新 XML 元素插入到现有的 XML 文档。
DOM 的缺陷是使用流方式处理文件 — 例如,转换为 HTML — 过于复杂,因为必须在结构内逐个遍历每个元素来处理文档。
此外,由于 DOM 在解析其间将整个 XML 文档载入到内存中,DOM 解析器会变得非常慢并且需要更多的内存。但 DOM 处理这样做也有一些好处;例如,在一次解析过程中,可以对使用 DOM 解析的 XML 文档进行多次处理。而使用 SAX,则需要多次重复解析过程才能获得相同的效果。
访问 参考资料,查找更多关于使用 DOM 和 SAX 的信息。
使用良好的 XML 编辑器
如果经常需要编写和使用 XML,那么必须拥有一个良好的 XML 编辑器。XML 编辑器不同于标准的文本编辑器,前者可以理解 XML 的结构和布局。XML 编辑器提供的丰富特性使处理 XML 更加简单,这些特性包括:
- 完成 — 为一个快完成的元素输入字符,编辑器可以自动帮您输入剩下的内容。
- 内容完成 — 如果对 XML 文件使用 DTD,那么编辑器可以为您填充并格式化部分内容。例如,在 contacts DTD 中,phone 元素的 type 属性是一个必需元素。使用智能 XML 编辑器,在创建 phone 标记时,该属性(值为空)将自动引入到文本中。
- 内联格式化 — 编辑器可以使您的 XML 更加易于阅读和理解。这可以在编辑时立即实现,也可以通过单独的格式命令实现。最终得到可以理解并可以更快速地标识的 XML。
- 内置验证 — 在输入内容时,编辑器可以验证 XML 文档的错误,在编辑器中立即突出显示各种问题,这样您就知道该如何解决这些问题。
- 内置翻译和转换 — 一些 XML 编辑器包括 XPath、XQuery 界面,某些情况下还包括 XSLT 和其他转换的界面,因此可以在编辑环境中查看转换结果。
- 学习和操作 — 有时您在 DTD 之前创建 XML 结构。在这种情况下,编辑器可以读取 XML 文件,学习它的结构并创建一个 DTD 进行验证,这样可以为您节省大量的时间和精力。
好的 XML 编辑器包括 Eclipse 和 oXygenXML,但是还有很多其他选择。
结束语
养成良好的 XML 处理习惯,一切将大有不同,包括利用 XML 提供的功能、打破 XML 标准进行验证的基础知识以及正确处理解析。本文可以帮助您学习这 10 个好习惯,从而提高处理 XML 文档和数据的效率。