XML 是一种数据格式。
读取XML文件内容,就需要解析器。
最普通的 XML 处理工作是
解析 XML 文档。解析包括读取 XML 文档并确定其结构和内容。
XML 编程的一个好处是可以使用开放源代码的、免费的 XML 解析器读取 XML 文档。
为了简化编写处理 XML 的 Java 程序,已经建立了多种编程接口。
主要的四个接口:
- Document Object Model (DOM,文档对象模型),Level 2
- Simple API for XML (SAX), Version 2.0
- JDOM, Jason Hunter 和 Brett McLaughlin 创立的一种简单 Java API
- Java API for XML Processing (JAXP)
使用 DOM、SAX 或 JDOM 处理 XML 文档的内容。
如果使用 DOM 或 SAX,则使用 JAXP 创建解析器。
如果使用 JDOM,则 JDOM 库为您创建解析器。
解析器的类型:
- 验证和非验证解析器
- 支持一种或多种 XML Schema 语言的解析器
- 支持 Document Object Model (DOM) 的解析器
- 支持 Simple API for XML (SAX) 的解析器
XML文件的类型:
结构良好的文档:这类文档符合 XML 基本规则(属性必须放在引号中、标签必须正确嵌套等等)。
有效文档:这些结构良好的文档同时还符合文档类型定义(DTD)或 XML Schema 所定义的规则。
无效文档:所有其他文档。
(如果您有一个 XML 文档符合 XML 的基本规则,那么它就是一个 结构良好的 文档。如果该文档还满足您的公司所定义的支出帐目文档规则,那么它也是 有效的。 )
如果 XML 解析器发现 XML 文档不是结构良好的,XML Specification 要求解析器报告一个致命错误。但验证是一个不同的问题。验证解析器 在解析时验证 XML 文档,而 非验证解析器 不验证文档。换句话说,如果一个 XML 文档是结构良好的,那么非验证解析器并不关心文档是否符合 DTD 或模式中定义的规则,甚至不关心该文档是否有这样的规则(原因:验证需要大量工作)
设置:
1首先请访问 Apache XML Project (http://xml.apache.org/xerces2-j/) 上的 Xerces XML 解析器主页。您也可以直接去 下载页面 (http://xml.apache.org/xerces2-j/download.cgi)。
2解压从 Apache 下载的文件。根据解析器版本的不同,这样将会创建名为 xerces-2_5_0
或者类似名称的目录。所需要的 JAR 文件(xercesImpl.jar
和 xml-apis.jar
)应该出现在 Xerces 根目录下。
3访问 JDOM 项目站点 并下载最新版本的 JDOM (http://jdom.org/)。
4解压从 JDOM 下载的文件,这样将建立名为 jdom-b9
或者类似名称的目录。所需要的 JAR 文件(jdom.jar
)应该在 build
目录中。
5最后请下载本教程的示例压缩文件 ,并解压该文件。
6把当前目录 (.
)、xercesImpl.jar
、xml-apis.jar
和 jdom.jar
添加到 CLASSPATH
变量中。
使用解析器:
1创建一个解析器对象
2使解析器指向您的 XML 文档
3处理结果
SAX:
Simple API for XML (SAX) API 是处理 XML 文档内容的一种替代方法。它的设计目标是更少的内存占用,但是把更多的工作交给了程序员。SAX 和 DOM 是互补的,有各自的适用环境。
当使用 SAX 解析器解析一个 XML 文档时,解析器在读取文档的过程中会生成一系列的事件。至于如何处理这些事件则取决于您。下面列出了一小部分您在 XML 文档时可能遇到的事件:
startDocument
事件。
- 对于每个元素,在元素开始时有
startElement
事件,元素结束时有 endElement
事件。
- 如果元素包含内容,对于文本将出现
characters
事件,对于子元素将出现 startElement
和 endElement
事件,依此类推。
endDocument
事件。
和 DOM 一样,SAX 解析器也忽略了某些细节,如属性出现的顺序。
SAX 和 DOM 都过于复杂,
和 DOM 类似,JDOM 也提供一个对象树表示 XML 文档,但是这些对象工作的方式对 Java 程序员更直观。要记住,JDOM 在背后包含使用普通 SAX 或 DOM 解析器的适配器;JDOM 对所有主要的(和几个次要的) Java XML 解析器都提供了适配器,因此不必担心您的 Java XML 解析器是否支持 JDOM。JDOM 在幕后使用一个解析器不需要您的干涉。
DOM
DOM 是处理 XML 文档结构的一种接口。作为一个 W3C 项目,DOM 的设计目标是提供一组对象和方法,使程序员的工作更轻松。
当使用 DOM 解析器解析一个 XML 文档时,您得到一个层次化的数据结构(DOM 树),它表示解析器在 XML 文档中发现的所有内容。然后您可以使用 DOM 函数操纵这棵树。您可以搜索树中的内容、移动分支、增加新的分支或者删除树的一部分。
DOM 解析器返回一个树状结构,这个 DOM 树包含一些 Node
。从 Java 语言的角度看,Node
是一个接口。Node
是 DOM 的基本数据类型,DOM 树中的所有事物都是这种或那种类型的 Node
。
DOM Level 1 还定义了 Node
接口的几种子接口:
Element
:表示源文档中的一个 XML 元素。
Attr
:表示 XML 元素的一个属性。
Text
:一个元素的内容。这意味着带有文本的元素包含文本节点孩子,元素的文本 不是 元素本身的一个属性。
Document
:表示整个 XML 文档。解析的每个 XML 文档中有且只有一个 Document
对象。给定一个 Document
对象就可以找到 DOM 树的根,从这个根可以使用 DOM 函数读和操纵树。
注意:“元素”和“标签”这两个词有不同的含义。元素 是指起始元素、结束元素以及两者之间的一切内容,包括属性、文本、注释以及子元素。标签 是一对<尖括号>和两者之间的内容,包括元素名和所有属性。比如 <p class="blue">
是一个标签,</p>
也是;而 <p class="blue">The quick brown fox</p>
是一个元素。
DOM 经常要用到以下方法:
Document.getDocumentElement()
:返回 DOM 树的根。(该函数是 Document
接口的一个方法,没有定义其他的 Node
子类型。)
Node.getFirstChild()
和 Node.getLastChild()
:返回给定 Node
的第一个和最后一个孩子。
Node.getNextSibling()
和 Node.getPreviousSibling()
:返回给定 Node
的下一个和上一个兄弟。
Element.getAttribute(String attrName)
:对于给定的 Element
,返回名为 attrName
的属性的值。如果需要 "id"
属性的值,可以使用 Element.getAttribute("id")
。如果该属性不存在,该方法返回一个空字符串 (""
)。
第一个应用程序是 DomOne.java
,这段简单的 Java 代码完成四件事:
- 扫描命令行得到 XML 文件名
- 创建一个解析器对象
- 告诉解析器解析命令行中给定的 XML 文件
- 遍历 DOM 结果树向标准输出打印各种节点的内容
扫描命令行相对而言非常简单。只需要查看一个参数,并假定该参数是一个文件名或 URI。如果命令行中没有参数,则打印一个错误消息并退出:
public static void main(String argv[])
{
if (argv.length == 0 ||
(argv.length == 1 && argv[0].equals("-help")))
{
System.out.println("\nUsage: java DomOne uri");
System.out.println(" where uri is the URI of the XML " +
"document you want to print.");
System.out.println(" Sample: java DomOne sonnet.xml");
System.out.println("\nParses an XML document, then writes " +
"the DOM tree to the console.");
System.exit(1);
}
DomOne d1 = new DomOne();
d1.parseAndPrint(argv[0]);
}
下面的代码说明了先创建一个工厂对象,然后工厂对象创建解析器:
public void parseAndPrint(String uri)
{
Document doc = null;
try
{
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
. . .
第 3 步:解析 XML 文件
现在您已经创建了解析器,告诉它解析哪个文件(或 URI)很简单:
doc = db.parse(uri);
第 4 步:打印 DOM 树的内容
最后一项任务是打印 DOM 树的内容。因为 DOM 树中的一切事物都是这种或那种类型的 Node
,您需要用递归的方法遍历并打印其中的内容。策略是调用该方法打印一个节点。该方法将打印那个节点,并对该节点的每个孩子调用自身。如果这些孩子还有孩子,该方法将对这些孩子调用自身。下面是一个例子:
if (doc != null)
printDomTree(doc);
. . .
}
public void printDomTree(Node node)
{
int type = node.getNodeType();
switch (type)
{
// print the document element
case Node.DOCUMENT_NODE:
{
System.out.println("<?xml version=\"1.0\" ?>");
printDomTree(((Document)node).getDocumentElement());
break;
}
printDomTree
方法以一个 Node
作为参数。如果该 Node
是一个文档节点,就打印一个 XML 声明,然后对文档元素(包含文档其他内容的元素)调用 printDomTree
。文档元素基本上总是有其他孩子,因此 printDomTree
也将对这些孩子调用自身。这种递归算法可以遍历整个 DOM 树并把内容打印到命令行中。
以下是 printDomTree
对元素节点的处理:
case Node.ELEMENT_NODE:
{
System.out.print("<");
System.out.print(node.getNodeName());
NamedNodeMap attrs = node.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
printDomTree(attrs.item(i));
System.out.print(">");
if (node.hasChildNodes())
{
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
printDomTree(children.item(i));
}
System.out.print("</");
System.out.print(node.getNodeName());
System.out.print('>');
break;
}
对于元素节点,您需要打印一个左尖括号和结点名称。如果该元素有属性,就对每个属性调用 printDomTree
。(技术上讲,这里可以使用 Attr.getName()
和 Attr.getValue()
打印属性,我这样做是为了说明 DOM 树中的一切都是 Node
。)一旦打印完所有的属性,就可以对该节点所包含的所有子元素再次调用 printDomTree
。最后一步是在处理完所有的子元素之后打印结束元素。
处理属性节点和文本节点很简单。对于属性,需要输出一个空格、属性名、等号、开双引号、属性值和闭双引号。(这里简化了,属性值可以使用单引号和双引号,如果愿意您完全可以修正这个问题。)
处理文本节点最简单,只需要输出文本就可以了。以下是这两种节点类型的代码:
case Node.ATTRIBUTE_NODE:
{
System.out.print(" " + node.getNodeName() + "=\"" +
((Attr)node).getValue() + "\"");
break;
}
case Node.TEXT_NODE:
{
System.out.print(node.getNodeValue());
break;
}
尽管这里只是打印 DOM 树的内容,但多数围绕着 DOM 建立的 XML 应用程序都使用这种四个步骤的模式,第四步是递归处理例程。
在您解压示例文件的目录下键入:
javac DomOne.java
(记住 Java 语言是区分大小写的。)如果没有任何错误,键入:
java DomOne sonnet.xml
解析并显示文件 sonnet.xml
。
如果一切都和预期的一样,您应该看到类似这样的内容:
C:\xml-prog-java>java DomOne sonnet.xml
<?xml version="1.0" ?>
<sonnet type="Shakespearean">
<author>
<lastName>Shakespeare</lastName>
<firstName>William</firstName>
<nationality>British</nationality>
<yearOfBirth>1564</yearOfBirth>
<yearOfDeath>1616</yearOfDeath>
</author>
<title>Sonnet 130</title>
<lines>
<line>My mistress' eyes are nothing like the sun,</line>
<line>Coral is far more red than her lips red.</line>
<line>If snow be white, why then her breasts are dun,</line>
<line>If hairs be wires, black wires grow on her head.</line>
. . .
</lines>
待续
摘自IBM在线学习