﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-辰o(^o^)o的专栏[除非注释原创，其它文章基本来源于网络]-文章分类-Java_Xml</title><link>http://www.blogjava.net/jackybu/category/837.html</link><description>&lt;a href="http://www.fastonlineusers.com"&gt;&lt;b&gt;&lt;font color=red&gt;共有&lt;script src=http://fastonlineusers.com/online.php?d=jackybu.blogjava.net&gt;&lt;/script&gt;人在同时阅读此Blog&lt;/font&gt;&lt;/b&gt;&lt;/a&gt;</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 04:12:08 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 04:12:08 GMT</pubDate><ttl>60</ttl><item><title>XML查询语言</title><link>http://www.blogjava.net/jackybu/articles/6156.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 15 Jun 2005 05:26:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/6156.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/6156.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/6156.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/6156.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/6156.html</trackback:ping><description><![CDATA[XML查询语言<BR>作者: Jonathan Robie等 译者: 王绪胜 写作日期:2000-4-1<BR>Jonathan Robie, Texcel, Inc.<BR>Joe Lapp, webMethods, Inc.<BR>David Schach, Microsoft Corporation<BR>合作:<BR>Michael Hyman, Microsoft Corporation<BR>Jonathan Marsh, Microsoft Corporation 
<P></P>
<P>摘要:<BR>XML查询语言(XQL) 是用于定位和过滤XML文档中元素和文本的符号。它是XSL模式语法的自然扩展，为指向特定的元素或查找具有指定特征的节点提供了简明的可以理解的符号。该建议于1998年9月提交给XSL工作小组(http://www.w3.org/Style/XSL/Group/1998/09/XQL-proposal.html)，在考虑XSL模式语法的扩展时作为基础输入。相关文档查询和转换XML说明了将本建议中的XML查询和转换语言基于XSL转换语言和模式语言扩展的优点。<BR>------------------------------------------------------------------------<BR>　<BR>目录:<BR>1. 导言<BR>2. XML模式<BR>2.1. 上下文<BR>2.2. 返回结果<BR>2.3. 集合<BR>2.4. 查询子女及后代<BR>2.5. 搜集元素子女<BR>2.6. 查找属性<BR>2.7. 分组<BR>2.8. 过滤<BR>2.9. 布尔表达式<BR>2.9.1. 布尔和与布尔或<BR>2.9.2. 布尔非<BR>2.10. 等值<BR>2.10.1. 比较和向量<BR>2.10.2. 比较和文字<BR>2.10.3. 比较过程中文字转化<BR>2.11. 方法<BR>2.11.1. 信息方法<BR>2.11.1.1. text()<BR>2.11.2. 集合索引函数<BR>2.11.3. 简化<BR>2.12. 集合中索引<BR>2.12.1. 查找集合中最末元素<BR>3. XQL扩展<BR>3.1. 命名空间<BR>3.1.1. 命名空间方法<BR>3.2. 查找属性集合<BR>3.3. 比较<BR>3.3.1. 数据类型<BR>3.3.2. 类型转换函数<BR>3.4. 任一(any)和所有(all)语义<BR>3.5. 并和交<BR>3.6. 集合方法<BR>3.6.1. 文档根部的DOM节点<BR>3.7. 聚集函数<BR>3.8. 附加方法<BR>3.9. 祖先<BR>3.10. 范围操作符<BR>Appendix A. XQL巴克斯范式<BR>A.1. 符号说明<BR>A.2. 术语<BR>A.2.1. 值<BR>A.2.2. 节点<BR>A.2.3. 源节点<BR>A.2.4. 文档顺序<BR>A.2.5. 索引<BR>A.2.6. 查找上下文<BR>A.2.7. 参考节点<BR>A.2.8. 参考节点集合<BR>A.2.9. 查找上下文集合<BR>A.2.10. 模式匹配<BR>A.3. 查询条件<BR>A.3.1. 条件产生规则<BR>A.3.2. 数字<BR>A.3.3. 文本<BR>A.3.4. 根<BR>A.3.5. 点<BR>A.3.6. 元素名称<BR>A.3.7. 属性名称<BR>A.3.8. 调用<BR>A.3.8.1. 参数<BR>A.3.8.2. 函数<BR>A.3.8.2.1. ancestor()<BR>A.3.8.2.2. attribute()<BR>A.3.8.2.3. comment()<BR>A.3.8.2.4. date()<BR>A.3.8.2.5. element()<BR>A.3.8.2.6. id()<BR>A.3.8.2.7. node()<BR>A.3.8.2.8. pi()<BR>A.3.8.2.9. textNode()<BR>A.3.8.2.10. true(), false()<BR>A.3.8.3. 方法<BR>A.3.8.3.1. baseName()<BR>A.3.8.3.2. count()<BR>A.3.8.3.3. end()<BR>A.3.8.3.4. index()<BR>A.3.8.3.5. namespace()<BR>A.3.8.3.6. nodeName()<BR>A.3.8.3.7. nodeType()<BR>A.3.8.3.8. nodeTypeString()<BR>A.3.8.3.9. prefix()<BR>A.3.8.3.10. text()<BR>A.3.8.3.11. rawText()<BR>A.3.8.3.12. value()<BR>A.4. 查询操作符<BR>A.4.1. 操作符优先级<BR>A.4.2. 操作符产生规则<BR>A.4.3. 分离<BR>A.4.4. 关联<BR>A.4.5. 否定<BR>A.4.6. 交<BR>A.4.7. 并<BR>A.4.8. 比较<BR>A.4.8.1. 操作数值－左手接收右手值<BR>A.4.8.2. 关系<BR>A.4.8.3. 类型转换<BR>A.4.8.4. 排序顺序<BR>A.4.8.5. 比较操作的值<BR>A.4.9. 路径<BR>A.4.9.1. 一元子女操作符<BR>A.4.9.2. 一元后代操作符<BR>A.4.9.3. 二元子女操作符<BR>A.4.9.4. 二元后代操作符<BR>A.4.9.5. 路径操作的值<BR>A.4.10. Bang操作符<BR>A.4.11. 子界<BR>A.4.12. 过滤<BR>A.4.13. 分组<BR>A.5. 查询<BR>A.6. 语法限制<BR>Appendix B. 示例数据<BR>------------------------------------------------------------------------</P>
<P><BR>1. 导言</P>
<P>XSL 模式语言( http://www.w3.org/TR/WD-xsl, section 2.6) 提供了易于理解的方式描述待处理节点。它是说明性而不是过程性语言，只需使用类目录结构的简单模式描述需要查找节点的类型。例如，book/author表示查找包含在book元素中的author元素。<BR>XQL(XML查询语言)提供对XSL模式语言的自然扩展。在XSL表示类型节点的基础上增加了布尔逻辑，过滤，节点集合索引等。<BR>XQL是特定为XML文档设计，它是一种通用查询语言，提供用于查询，定位和模式的简单语法。XQL精炼，简单，具有强大的功能。<BR>请注意术语XQL是本建议中对查询语言的工作术语，并不意味着我们准备永久使用该术语。</P>
<P>对于论证XQL及其理论背景的优秀论文，请参照Jonathan Robie的论文The Design of XQL。</P>
<P>以下是XQL的设计目标：<BR>XQL字符串必须紧凑；<BR>XQL应该易于打字和阅读；<BR>XQL语法应简单，以便用于简单和通常情况；<BR>XQL应该用易于嵌入程序，脚本和XML或HTML属性的字符串表述；<BR>XQL应易于分析；<BR>XQL应该用嵌入URL的字符串表述；<BR>XQL应能够指定XML文档中可能的任何路径和指定任何条件集合以查找路径上的节点；<BR>XQL应能唯一指定XML文档中的所有节点；<BR>XQL查询可以返回任何数量的结果，包括0；<BR>XQL查询是说明性，而非过程性。即：说明查找什么，而不是如何查找。这一点尤为重要，因为查询优化器必须能够自由选择索引和其他结构以便高效地找到结果。<BR>XQL查询可以在文档的任何级别开始计算，而不必从文档根元素开始导航；<BR>XQL查询按照文档顺序，无重复地返回结果。<BR>尽管XQL是XSL模式语言的超集，XQL设计用于许多情况，可以用于提供节点的链接，查找信息和许多其他应用。<BR>XQL是从文档中查询信息的符号，这些信息可能是一个节点集合、节点关系信息或导出值。本说明书没有指定输出的格式，查询结果可能是单个节点、节点列表、XML文档、其他结构序列。即：XQL并不指定返回的二进制格式，而是逻辑结果。<BR>在一些实现中，查询结果可能是一个XML文档或者是传回XQL的树。在其他情况下，结果可能是其他类型的结构，例如：指向节点指针的集合。因此，封闭性并不能保证，如果实现方案返回XML文档，则该文档必须格式良好，从而保证了封闭性。</P>
<P><BR>2. XML模式<BR>本节描述XQL核心符号。所有的XQL实现必须满足这些特征，而且在不同技术使用中作为基本功能。<BR>XQL基本语法模仿URI目录导航语法，但不是通过物理文件结构指定导航，而是通过XML树的元素。<BR>例如，下例URI表示在bar目录中查找foo.jpg文件：<BR>bar/foo.jpg<BR>类似地，在XQL中，下例表示查找在baz元素中查找fuz元素集合：<BR>baz/fuz<BR>在整篇文章中，涉及许多示例，它们都参照附录Sample Data中的数据。</P>
<P>2.1. 上下文<BR>“上下文”是当前查询操作的节点集合。该术语在附录A中正式定义。要理解上下文的概念，考虑包含多个节点的树。从根节点开始查找所有命名为“X”的节点将返回结果集合，从树枝开始查找所有命名为“X”的节点将返回不同的结果集合。因此，查询结果取决于执行时的上下文。有许多不同方法可以用于指定查询的输入上下文。<BR>XQL允许查询在当前上下文和“根上下文”选择作为输入上下文。“根上下文”是一个包含文档中最根部元素的上下文。缺省地，查询使用当前上下文，以“/”(前倾斜)作为前缀的查询使用根上下文。查询可以随意使用“./”(点，前倾斜)作为前缀显式说明使用当前上下文。这两个符号都是文件系统导航符号的模拟。“./”前缀只在一种情况下必须存在。使用“//”操作符表示递归后代。当该操作符出现在查询开头时，初始的“/”引起相对于文档或存储根部的递归后代执行。前缀<BR>“.//<BR>”允许查询当前上下文的递归后代。<BR>示例：<BR>在当前上下文中查找所有author元素。由于句点不单独使用，该例向前应用了其他特征<BR>：<BR>./author<BR>请注意，该例等价于：<BR>author<BR>查找文档中的根元素(bookstore)：<BR>/bookstore<BR>在当前文档中查找所有出现在任何地方的author元素：<BR>//author<BR>在文档根部查找所有book元素的style属性值与bookstore元素specialty属性值相等的所有book元素。<BR>book[/bookstore/@specialty = @style]</P>
<P>2.2. 返回结果<BR>XQL表达式返回的集合在定义范围内保持文档顺序，层次结构和标识。即：元素集合总是无重复地以文档顺序返回，属性集合将无重复地返回，但由于属性无顺序地定义，返回集合并不意味着顺序。</P>
<P>2.3. 集合</P>
<P>带有特定标记名称的所有元素集合用标记名称自身表示。元素查询可以被限定在当前上下文“./”，但当前上下文是认定的，通常无需显式指定。<BR>示例:<BR>查找所有first-name元素。下列例子是等价的：<BR>./first-name<BR>first-name<BR>查找所有未限定的book元素：<BR>book<BR>查找所有first.name元素：<BR>first.name</P>
<P><BR>2.4. 查询子女和后代</P>
<P>某些类型的元素集合可以用路径操作符（“/”或“//”）指定。路径操作符将左部作为查询来源的参数集合，右部表示需要查询的元素。子女操作符（“/”）查询左部元素集合的直接子女集合，后代操作符（“//”）查询左部元素集合的任意后代集合。事实上，“//”可以被视为一个或者多个层次结构的替代方案。注意路径操作符在查询过程中改变上下文。通过将其串在一起，用户可以“深钻”进入文档内部。<BR>示例:<BR>在author元素中查找所有first-name元素。首先查找当前环境的author子女，然后查找相对于author元素环境的first-name子女：<BR>author/first-name<BR>在bookstore元素一级或多级以下（任意后代），查找所有title元素：<BR>bookstore//title<BR>注意，该例与下述查找所有bookstore元素的孙子title元素的查询并不相同：<BR>bookstore/*/title<BR>查找在book/excerpt元素中任意位置的emph元素，而且book/excerpt元素在<BR>bookstore元素中任意位置：<BR>bookstore//book/excerpt//emph<BR>在当前环境一级或多级以下查找所有title元素。注意该情况本来是要求句点符号的唯一情况：<BR>.//title</P>
<P><BR>2.5. 搜集元素子女</P>
<P>可以不用标记名称而用“*”集合引用一个元素。“*”集合不管子女元素的标记名称，返回当前环境的所有子女元素。<BR>示例:<BR>查找author元素的所有子女元素：<BR>author/*<BR>查找book元素的所有last-name孙子元素：<BR>book/*/last-name<BR>查找当前环境的所有孙子元素：<BR>*/*<BR>查找具有指定属性的所有元素。注意该例使用了在过滤一节涉及的子查询和在查找属性一节讨论的属性：<BR>*[@specialty]</P>
<P><BR>2.6. 查找属性</P>
<P>属性名称用“@”符号开头。XQL设计公平对待属性和子元素，在可能的情况下，两种类型的能力是等价的。<BR>注意：属性不能包含子元素。因此，在查询中属性不能使用路径操作符。否则将导致语法错误。同样地，属性天生没有顺序，索引不能用于属性。<BR>示例:<BR>查找当前环境的style属性：<BR>@style<BR>在当前环境中查找price元素的exchange属性：<BR>price/@exchange<BR>下例不是有效的查询：<BR>price/@exchange/total<BR>查找所有包含style 属性的book元素。注意该例使用了在过滤一节涉及的子查询：</P>
<P>book[@style]<BR>查找所有book元素的style属性：<BR>book/@style</P>
<P><BR>2.7. 分组</P>
<P>为使操作清晰清晰或者常用的优先级不足以表示某个操作时，圆括号可以用于将集合操作符分组。</P>
<P><BR>2.8. 过滤</P>
<P>通过对集合增加过滤子句“[ ]”，可以对任何集合进行限制和分枝。过滤是SQL中包含ANY语义的WHERE子句的模拟。过滤子句中包含了一个查询，称为子查询。子查询计算出布尔值，对集合中的每一个元素进行测试。集合中未通过子查询测试的元素将从结果集合中省略。<BR>为方便起见，如果集合放于过滤之中，如果集合中包含元素，则产生布尔值TRUE；如果集合为空，则产生FALSE。本质上，如author/degree的表达式表示集合到布尔值的转换函数，象如下虚构的“存在一个”方法：<BR>author[.there-exists-a(degree)]<BR>注意，在表达式的给定级别上可以出现任何数目的过滤，但空过滤是不允许的。<BR>示例:<BR>查找至少包含一个excerpt元素的book元素：<BR>book[excerpt]<BR>查找至少包含一个excerpt元素的book元素的所有title：<BR>book[excerpt]/title<BR>查找至少包含一个excerpt元素的book元素的author，而且author元素至少包含一<BR>个deg<BR>ree元素：<BR>book[excerpt]/author[degree]<BR>查找包含author元素的book元素，而且author元素至少包含一个degree元素：<BR>查找包含excerpt和title的所有book元素：</P>
<P><BR>2.9. 布尔表达式</P>
<P>布尔表达式可以在子查询中使用。例如，可以使用布尔表达式查找特定值的节点集合，或者特定范围的节点集合。布尔表达式采取${op}$的形式，而{op}可以是任何{b|a}形式的表达式－即：操作符接收左值和右值参数，返回布尔结果。应用可以根据需要提供附加的布尔操作符，但实现结果令人气馁。注意XQL表达式一节定义了附加的布尔操作符。操作符是大小写敏感的。</P>
<P>2.9.1. 布尔与和布尔或</P>
<P>$and$和$or$用于执行布尔与和布尔或。<BR>布尔操作符，和分组括号结合起来，可以用于构成非常复杂的逻辑表达式。<BR>注意空格是无意义的，可以被省略，或者如下所示包含空格，用于增加可读性。<BR>示例:<BR>查找至少包含一个degree元素和一个award元素的author元素：<BR>author[degree $and$ award]<BR>查找至少包含一个degree元素或者award元素，而且包含至少一个publication元素的author元素：<BR>author[(degree $or$ award) $and$ publication]</P>
<P>2.9.2. 布尔非</P>
<P>$not$是用于子查询中表达式值求反的布尔操作符。<BR>示例:<BR>查找至少包含一个degree元素，而且不包含publication元素的author元素：<BR>author[degree $and$ $not$ publication]<BR>查找包含publication元素，而且不包含degree元素和award元素的author元素：<BR>author[$not$ (degree $or$ award) $and$ publication]</P>
<P>2.10. 等值</P>
<P>“=” 符号用于相等判断，“!=”用于不相等判断。作为选择，$eq$和$ne$ 也可用于相等和不相等。单引号或双引号可以用于在表达式中分隔字符串，使得在脚本语言中创建或传递XQL变得更为容易。<BR>对于比较元素值而言，暗含了value()方法。也就是说，last-name &lt; ‘foo’实际<BR>上表示last-name!value() &lt; foo 。<BR>请注意，过滤总是和上下文相关的。即：表达式book[author]表示对每一个找到的book元素，查看其是否包含author子元素。同样地，book[author = Bob ] 表示对每一个找到的book元素，查看其是否包含命名为author，且值为’Bob’的子元素。你也可以通过使用“.”(句点) 查看当前上下文的值。例如，book[. = Trenton ]表示对每一个找到的book元素，查看其值是否等于’ Trenton’。<BR>示例:<BR>查找last name等于Bob的所有author：<BR>author[last-name = Bob ]<BR>author[last-name $eq$ Bob ]<BR>查找from属性不等于 Harvard 的所有author：<BR>degree[@from != Harvard ]<BR>degree[@from $ne$ Harvard ]<BR>查找last-name 与/guest/last-name 相同的所有author：<BR>author[last-name = /guest/last-name]<BR>查找文本为’ Matthew Bob’ 的所有author：<BR>author[. = Matthew Bob ]</P>
<P>2.10.1. 比较与向量</P>
<P>比较的左值可以是一个向量或数量。而比较的右值必须是一个数量或者一个可以在运行时转换为数量的值。<BR>如果比较的左值是一个集合，那么any(exists)语义用于比较操作符。也就是说，如果集合中的任何元素满足条件，则比较结果为真。</P>
<P>2.10.2. 比较与文字常量</P>
<P>表达式的左值不能是文字常量。也就是说，’1’=a是不允许的。</P>
<P>2.10.3. 比较过程的文字常量转换</P>
<P>所有元素和属性都是字符串，但是许多时候要求数字比较。<BR>如果右值是一个属性，text(lvalue)与text(rvalue)进行比较。<BR>如果右值是一个文字常量，则运用下列规则：<BR>常量类型 比较 示例<BR>字符串<BR>text(lvalue) op text(rvalue)<BR>a &lt; foo<BR>整数<BR>(long) lvalue op (long) rvalue<BR>a &lt; 3<BR>实数<BR>(double) lvalue op (double) rvalue<BR>a &lt; 3.1<BR>不支持指数符号。<BR>当元素包含类型时，参看数据类型 关于类型转换的讨论。</P>
<P><BR>2.11. Methods</P>
<P>XQL为高级集合操作提供方法。这些方法提供节点的特定集合(查看集合方法)，同时提供关于集合和节点的信息。<BR>方法的形式为{方法}(参数表)。<BR>考虑查询book[author]。它将发现包含author元素的所有book。一般来讲，我们称对应于特定author的book元素为该author的参考节点。也就是说，每一个所检查的author元素都是其中一个book元素的author(关于参考节点和其他术语的更为彻底的定义，请查看<BR>Annotated XQL BNF附录)。方法应用于参考节点。<BR>例如：text()方法返回节点内包含的文字，不包括所有结构(也就是说，是包含在元素及其后代的所有文字节点的连接)。下列表达式将返回所有名为’Bob’的author：<BR>author[text() = Bob ]<BR>下例将返回包含first-name子节点，且该子节点的文字为’Bob’的所有author:<BR>author[first-name!text() = Bob ]<BR>下例将返回包含名为’Bob’的子节点的所有author：<BR>author[*!text() = Bob ]<BR>方法名称是大小写敏感的。</P>
<P><BR>2.11.1. 信息方法</P>
<P>下列方法提供关于集合内节点的信息。这些方法返回字符串或数字，而且可以用于连接子查询内的比较操作符。<BR>text()<BR>包含在元素内的文字。该方法连接所有后代节点的文字，不包括标记名称或属性值，注释等。而且将如text()中讨论一样整理字符串。(返回字符串)<BR>value()<BR>返回一个元素值的类型转换版本(参看Datatypes)。如果数据类型不支持或者该数据类型不提供，返回结果与text()相同。<BR>nodeType()<BR>返回一个指示节点类型的数字：<BR>元素 1<BR>属性 2<BR>文字 3<BR>处理指令 7<BR>注释 8<BR>文档 9<BR>nodeName()<BR>节点的标记名称，包括命名空间前缀(返回字符串)。</P>
<P>2.11.1.1. text()</P>
<P>text()方法连接所有节点后代的文字，根据节点属性可选地规范化空白空间。如果节点或者最近的祖先节点的xml:space属性设为’preserve’，空白空间将被保留。当空白空间规范化时，将在整个字符串范围内进行。空格用于分隔节点间的文字。当实体引用在文档中使用时，扩展时空格不出现在实体引用的周围。<BR>示例:<BR>查找last name为’Bob’的author：<BR>author[last-name!text() = Bob ]<BR>该查询等价于：<BR>author[last-name = Bob ]<BR>查找值为 Matthew Bob 的author：<BR>author[text() = Matthew Bob ]<BR>author[. = Matthew Bob ]</P>
<P>2.11.2. 集合索引函数</P>
<P>index() 返回父节点内该节点的索引号。索引号是从0开始的，所以0是第一个元素(返回一个数字) 。<BR>查找前三个degree：<BR>degree[index() $lt$ 3]<BR>注意索引函数与父节点相关。考虑下列数据：<BR>&lt;x&gt;<BR>&lt;y/&gt;<BR>&lt;y/&gt;<BR>&lt;/x&gt;<BR>&lt;x&gt;<BR>&lt;y/&gt;<BR>&lt;y/&gt;<BR>&lt;/x&gt;<BR>下列表达式在每一个x返回第一个y：<BR>x/y[index() = 0]</P>
<P>2.11.3. 简写</P>
<P>对于比较的目的而言，如果省略了方法名，value()方法是暗含的。换句话说，当两个项目进行比较时，比较在两个项目的值之间进行。请记住在没有类型信息时，value()返回文字。<BR>下列例子是等价的：<BR>author[last-name!value() = Bob $and$ first-name!value() = Joe ]<BR>author[last-name = Bob $and$ first-name = Joe ]<BR>price[@intl!value() = canada ]<BR>price[@intl = canada ]</P>
<P><BR>2.12. 集合内索引</P>
<P>XQL很容易在节点集合中发现一个特定的节点。简单地将索引号放在方括号(‘[’和‘]’)里即可。序号是从0开始的。例如，下例查找第一个author元素：<BR>author[0]<BR>下例查找含有first-name子元素的第三个author元素：<BR>author[first-name][2]<BR>注意索引是相对于父节点。换句话说，考虑下列数据：<BR>&lt;x&gt;<BR>&lt;y/&gt;<BR>&lt;y/&gt;<BR>&lt;/x&gt;<BR>&lt;x&gt;<BR>&lt;y/&gt;<BR>&lt;y/&gt;<BR>&lt;/x&gt;<BR>下列表达式将从每一个x中返回第一个y：<BR>x/y[0]<BR>下例将返回x内所有y组成的集合中的第一个y：<BR>(x/y)[0]<BR>下例将返回第一个x的第一个y：<BR>x[0]/y[0]</P>
<P>2.12.1. 在集合中返回最后的元素</P>
<P>end()方法对集合的最后一个元素返回真。注意end()是相对父节点的。<BR>示例:<BR>查找最后的book：<BR>book[end()]<BR>查找每一个book元素的最后的author：<BR>book/author[end()]<BR>在book的所有author构成的集合中，查找最后的author：<BR>(book/author)[end()]</P>
<P>3. XQL扩展</P>
<P>XQL功能提供在XQL客户端所需的最小功能集合。XQL扩展描述了扩展XQL能力的额外的功能。</P>
<P><BR>3.1. 命名空间</P>
<P>在查询中建立命名空间是需要的。由于命名空间是局部范围，这一点尤其重要。因<BR>此，<BR>XQL需要一个可以为查询的全局范围或者查询内的局部范围建立命名空间的机制。</P>
<P>尽管我们不得不建立明确的命名空间语法，该语法必须满足下列要求：<BR>该语法必须能够设定一个缺省的命名空间用于查询内部；<BR>该语法必须能够设定一个命名空间用于任何指定的范围；<BR>该语法必须能够设定一个命名空间用于任何级别的查询。换句话说，人们应该能够<BR>在查询开始设定命名空间，即使它们并没有用于查询内部较深的元素；<BR>该语法必须能够建立一个匹配命名空间的长名称的前缀。<BR>如果没有在查询中指定命名空间，前缀则用于匹配。<BR>如果命名空间定义在session上，而不是在每一个查询基础上，XQL处理器可以更有<BR>效的地执行。本规范没有为指定用于整个session的命名空间的集合描述API或XML格式。这是由应用定义的。</P>
<P>3.1.1. 命名空间方法</P>
<P>下列方法可以用于节点，返回命名空间信息。<BR>baseName()<BR>返回节点的名称部分，不包括前缀。<BR>Namespace()<BR>返回节点命名空间的URI。<BR>Prefix()<BR>返回节点的前缀。<BR>示例:<BR>查找所有未加限定的book元素。注意该查询不返回my:book元素：<BR>book<BR>查找所有包含前缀’my’的book元素。注意该查询不返回未加限定的book元素：<BR>my:book<BR>查找所有包含前缀’my’而且含author子元素的book元素：<BR>my:book[author]<BR>查找所有包含前缀’my’的book元素，其中包含author子元素而且author子元素也<BR>包含<BR>前缀’my’：<BR>my:book[my:author]<BR>查找所有包含前缀’my’的所有元素：<BR>my:*<BR>在所有命名空间下查找所有的book元素：<BR>*:book<BR>在所有命名空间下查找所有元素：<BR>*<BR>在book元素中查找含前缀’my’的style属性：<BR>book/@my:style</P>
<P>3.2. 查找属性集合</P>
<P>用@*可以返回元素的所有属性，这一点对那些将属性视为记录域的应用程序具有潜在的作用。<BR>示例:<BR>查找所有当前元素上下文的所有属性：<BR>@*<BR>查找所有命名空间下的所有style属性：<BR>@*:style<BR>查找命名空间’my’下的所有属性，包括命名空间’my’下元素的未加限定的属性<BR>：<BR>@my:*</P>
<P>3.3. 比较</P>
<P>二进制比较操作符集合可以用于比较数字和字符串，结果返回布尔值。$lt$、 $le$、$gt$、 $ge$分别用于小于、小于或等于、大于、大于或等于。这些操作符也可用于大小写不敏感的形式：$ieq$, $ine$, $ilt$, $ile$, $igt$, $ige$。<BR>单引号或双引号可以用于在表达式中分隔字符串，使得在脚本语言中创建或传递XQL变得更为容易。<BR>所有元素和属性都是字符串，但是许多时候要求数字比较。关于类型转换信息，查看比较过程中文字常量转换和数据类型。<BR>&lt;、&lt;=、&gt;和&gt;=是$lt$, $le$, $gt$和$ge$的简写方式。<BR>示例:<BR>查找last name是’Bob’而且price&gt;50的所有author元素：<BR>author[last-name = Bob $and$ price $gt$ 50]<BR>查找from属性值不等于’Harvard’的所有author：<BR>author[@from != Harvard ]<BR>查找last name开始于’M’或更大字母的所有author：<BR>author[last-name $ge$ M ]<BR>查找last name开始于’M’、’m’或更大字母的所有author：<BR>author[last-name $ige$ M ]<BR>查找前两个book元素：<BR>book[index() $le$ 2]<BR>查找publications超过10的所有author：<BR>author[publications!count() $gt$ 10]</P>
<P>3.3.1. 数据类型</P>
<P>如果提供了数据类型，value()函数使用该类型决定元素的类型。对于比较目的而言，左值总是转换为右值的类型，因此保证了类型在比较过程中不发生变化。任何不能强制转换的左值将从结果集中省略。</P>
<P>3.3.2.类型转换函数</P>
<P>XQL提供函数用于值的类型转换。类型转换函数可以转换文字常量或集合。当前，只提供了date函数。它将值转换为日期，该值必须是具有日期格式的XML数据类型。<BR>示例:<BR>查找所有在1995年1月5日前出版的book：<BR>books[pub_date &lt; date( 1995-01-01 )]<BR>查找出版日期(pub_date)早于属性first中存储的值的所有book：<BR>books[pub_date &lt; date(@first)]</P>
<P>3.4. 任一(any)和所有(all)语义</P>
<P>作者可以通过$any$和$all$关键字显式指示是否使用任一(any)和所有(all)语义。</P>
<P>$any$表示如果集合中的任一元素满足条件，则条件为真。$all$表示如果集合中的所有元素满足条件，则条件为真。<BR>$any$和$all$关键字可以出现在任何表达式前。<BR>示例：<BR>查找其中一个last name是’Bob’的所有author元素：<BR>author[last-name = Bob ]<BR>author[$any$ last-name = Bob ]<BR>查找所有last name都不是’Bob’的所有author元素：<BR>author[$all$ last-name != Bob ]<BR>查找第一个last name是’Bob’的所有author元素：<BR>author[last-name[0] = Bob ]</P>
<P>3.5. 并(Union)和交(intersection)</P>
<P>$union$操作符返回多个元素，返回元素没有重新排序，没有重复元素，双重指定是暗含的。选择列表中的所有元素必须是当前选择上下文的后代。注意：由于这是并操作，返回集合每一个类型列表中可能包含0或更多的元素。为限定返回集合至少包含列表中每一个元素，使用在过滤一节讨论的过滤。是$union$的简写。<BR>$intersect$操作符返回在两个集合之间的公共元素。元素无需重新排序。<BR>示例：<BR>查找所有的first-name和last-name：<BR>first-name $union$ last-name<BR>从bookstore中查找所有book和magazine：<BR>bookstore/(book | magazine)<BR>查找所有book和所有author：<BR>book $union$ book/author<BR>查找所有的first-name、last-name、来自于book或magazine内author的degree：</P>
<P>(book $union$ magazine)/author/(first-name $union$ last-name $union$ degree)</P>
<P>查找author/first-name等于’Bob’的所有book和price小于10的所有magazine：</P>
<P>book[author/first-name = Bob ] $union$ magazine[price $lt$ 10]</P>
<P>3.6. 集合方法</P>
<P>集合方法为文档中不同类型节点提供访问方式。这些集合可以被约束和索引。集合返回满足特定条件的参考节点的子节点集合。<BR>textNode()<BR>文字节点的集合。<BR>Comment()<BR>注释节点的集合。<BR>Pi()<BR>处理指示节点的集合。<BR>Element([ name ])<BR>所有元素节点的集合。如果提供了可选的文字参数，将只返回匹配特定名称的元素子节<BR>点。The collection of all element nodes. If the optional text <BR>parameter is p<BR>rovided, it only returns element children matching that particular <BR>name.<BR>Attribute([ name ])<BR>所有属性节点的集合。如果提供了可选的文字参数，将只返回匹配特定名称的属性节点。<BR>Node()<BR>返回所有非属性节点。<BR>示例：<BR>查找当前上下文中每一个p元素中的第二个文本节点：<BR>p/textNode()[1]<BR>查找文档中第二个注释节点。关于文档根部上下文设置和细节，请查看上下文：<BR>//comment()[1]</P>
<P>3.6.1. 文档根部的DOM节点</P>
<P>请注意，在DOM中文档对象包含注释、处理指令、声明、以及术语所称的“根元素”。XQL使用术语“文档实体”表示DOM树的根―文档对象－而不是“根元素”。 在用于定位的文档实体层次允许注释等。<BR>示例：<BR>查找文档实体层次的所有注释<BR>/comment()</P>
<P><BR>3.7. 聚合方法</P>
<P>下列方法基于一个集合产生聚合结果。<BR>count() 返回集合中的节点数目。</P>
<P><BR>3.8. 其他方法</P>
<P>下述方法返回指示节点类型的字符串。<BR>nodeTypeString() 返回用于指示节点类型的下列字符串之一：<BR>document<BR>element<BR>attribute<BR>processing_instruction<BR>comment<BR>text</P>
<P><BR>3.9. 祖先(ancestor)</P>
<P>Ancestor查找满足查询的最近祖先。结果返回一个元素或空集。<BR>ancestor(query) 满足提供查询的最近祖先。.<BR>示例：<BR>查找当前元素的最近的book祖先：<BR>ancestor(book)<BR>查找包含在book元素最近的author祖先：<BR>ancestor(book/author)</P>
<P><BR>3.10. 脚标操作符</P>
<P>可以返回元素的排列。为实现该功能，在脚标操作符(方括号)内指定一个表达式而不是单个值。该表达式可以是逗号分隔的由下列条目组成的列表。<BR>n<BR>返回第n个元素<BR>-n<BR>返回从最后的元素倒数第n个元素。如：-1表示最后一个元素，-2表示倒数第2个元素。</P>
<P>M $to$ n<BR>返回从m到n的元素，其中m,n是包含的。<BR>示例：<BR>查找第一个和第四个author元素：<BR>author[0,3]<BR>查找第一个到第四个author元素：<BR>author[0 $to$ 3]<BR>查找第一个，第三个到第五个和最后一个author元素：<BR>author[0,2 $to$ 4, -1]<BR>查找最后一个author元素：<BR>author[-1]</P><img src ="http://www.blogjava.net/jackybu/aggbug/6156.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-06-15 13:26 <a href="http://www.blogjava.net/jackybu/articles/6156.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SAX之Java实现学习笔记</title><link>http://www.blogjava.net/jackybu/articles/2559.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Mon, 28 Mar 2005 08:15:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2559.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2559.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2559.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2559.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2559.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 本文假设读者对XML有些了解&nbsp;首先，先给出一个比较基本的处理xml文件的程序。你不必细看，直接跳过即可。需要时可以返回来看。&nbsp;&nbsp;Echo01.java&nbsp;import java.io.*;&nbsp;import org.xml.sax.*;import org.xml.sax.helpers.DefaultHandle...&nbsp;&nbsp;<a href='http://www.blogjava.net/jackybu/articles/2559.html'>阅读全文</a><img src ="http://www.blogjava.net/jackybu/aggbug/2559.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-28 16:15 <a href="http://www.blogjava.net/jackybu/articles/2559.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用SAX处理XML文档</title><link>http://www.blogjava.net/jackybu/articles/2558.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Mon, 28 Mar 2005 08:09:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2558.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2558.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2558.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2558.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2558.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width=168 align=right border=0>
<TBODY>
<TR><!-- Sidebar Gutter-->
<TD width=8><IMG height=21 alt="" src="http://www-900.cn.ibm.com/developerWorks/cn/i/c.gif" width=5></TD>
<TD width=160><!-- Start TOC-->
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www-900.cn.ibm.com/developerWorks/cn/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerWorks/cn/i/bg-gold.gif height=5><B>内容：</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www-900.cn.ibm.com/developerWorks/cn/i/c.gif" width=160></TD></TR>
<TR>
<TD align=right>
<TABLE cellSpacing=0 cellPadding=3 width="98%" border=0>
<TBODY>
<TR>
<TD><A href="http://www.blogjava.net/jackybu/articles/2558.html#1">SAX的基本情况</A></TD></TR>
<TR>
<TD><A href="http://www.blogjava.net/jackybu/articles/2558.html#2">理解并使用SAX</A></TD></TR>
<TR>
<TD><A href="http://www.blogjava.net/jackybu/articles/2558.html#3">一个实例</A></TD></TR>
<TR>
<TD><A href="http://www.blogjava.net/jackybu/articles/2558.html#4">使用DefaultHandler</A></TD></TR>
<TR>
<TD><A href="http://www.blogjava.net/jackybu/articles/2558.html#5">使用过滤器</A></TD></TR>
<TR>
<TD><A href="http://www.blogjava.net/jackybu/articles/2558.html#6">一些值得注意的问题</A></TD></TR>
<TR>
<TD><A href="http://www.blogjava.net/jackybu/articles/2558.html#7">SAX与DOM的比较</A></TD></TR>
<TR>
<TD><A href="http://www.blogjava.net/jackybu/articles/2558.html#8">附录SAX的版权声明</A></TD></TR>
<TR>
<TD><A href="http://www.blogjava.net/jackybu/articles/2558.html#author1">关于作者</A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><!-- End TOC--><!-- Start Related Content Area -&#45;
        
<table width="160" border="0" cellspacing="0" cellpadding="0">
        
<tr><td bgcolor="#000000" height="1" width="160"><img src="/developerWorks/cn/i/c.gif" width="160" height="1" alt=""/></td></tr>
        
<tr><td height="5" background="/developerWorks/cn/i/bg-gold.gif" align="center"><b>相关内容：</b></td></tr>
        
<tr><td bgcolor="#666666" height="1" width="160"><img src="/developerWorks/cn/i/c.gif" width="160" height="1" alt=""/></td></tr>
        
<tr><td align=right>
            
<table width="98%" border="0" cellspacing="0" cellpadding="3">
            
<tr><td><a href=#1>TCP/IP 介绍</a></td></tr>
            
<tr><td><a href=#1>TCP/IP 介绍</a></td></tr>
            </table>
        </td></tr></table>
-&#45; End TOC-->
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=150 bgColor=#000000 colSpan=2 height=2><IMG height=2 alt="" src="http://www-900.cn.ibm.com/developerWorks/cn/i/c.gif" width=160></TD></TR>
<TR>
<TD width=150 bgColor=#ffffff colSpan=2 height=2><IMG height=2 alt="" src="http://www-900.cn.ibm.com/developerWorks/cn/i/c.gif" width=160></TD></TR></TBODY></TABLE><!-- END STANDARD SIDEBAR AREA--></TD></TR></TBODY></TABLE><!-- START SUBTITLE AND CONTENT-->
<P><A href="http://www-900.cn.ibm.com/developerWorks/cn/xml/x-saxhandle/index.shtml#author1">王晓强</A> (<A href="mailto:forff@sina.com">forff@sina.com</A>)<BR><!-- <a href="#author2">Author name</a> (<a href="mailto:userid@us.ibm.com">email address</a>)<br /> -->2001 年 11 月</P>
<BLOCKQUOTE>这里我将向大家介绍处理XML文档的另一个重要接口SAX（Simple API for XML）。其中包括它的基本情况，它的API，一个开发实例，实际开发中一些需注意的问题，以及它与DOM的对比。</BLOCKQUOTE>
<P><A name=1><SPAN class=atitle2>SAX的基本情况</SPAN></A><BR>SAX同DOM一样也是一个访问XML文档的接口。SAX是Simple API for XML的缩写。它不像DOM那样是W3C的推荐标准。它是由XML-DEV邮件列表的成员开发维护，由David Megginson领导（david@megginson.com）的一个Public Domain软件。SAX是一个彻底的自由软件，它的作者放弃了对它的所有权利，并且它也被许可用于任何目的（在文章最后附录了它的版权声明）。</P>
<P>到现在为止SAX的版本已经发展到2.0。在这个最新版本中增加了对名称空间（Namespaces）的支持，而且可以通过对features以及properties的设置来对解析器做全面的配置，这其中包括设置解析器是否对文档进行有效性验证，以及怎样来处理带有名称空间的元素名称等。SAX1中的接口已经不再使用了，这里只会讨论有关SAX2的开发。在本文中提到SAX只是指SAX 2。另外，本文的所有例子都是用java编写，SAX解析器也使用的是JAVA版本。</P>
<P>像DOM一样,SAX并不是一个实际可以使用的XML文档解析器，而是其他兼容SAX的解析器要实现的接口和帮助类的集合。如果你想使用SAX的话，你必须满足下面的要求： 
<OL class=n01>
<LI>系统中包含Java 1.1 或者更高版本。 
<LI>在Java classpath中包含进你的SAX类库。 
<LI>在Java classpath中包含进你要使用的兼容SAX的XML解析器类库。</LI></OL>
<P></P>
<P>实现了SAX的解析器有很多，比如Apache的Xerces，Oracle的XML Parser等等。在本文中的例子程序使用的都是Xerces解析器,你可以从 http://xml.apache.org 得到它。让我们下载得到xerces.jar文件然后将其加入到classpath中去，这样我们就已经建立好环境（在xerces.jar中已经包含了SAX接口，所以不必特意再去寻找SAX类库）。</P>
<P>在SAX API中有两个包，org.xml.sax和org.xml.sax.helper。其中org.xml.sax中主要定义了SAX的一些基础接口，如XMLReader、ContentHandler、ErrorHandler、DTDHandler、EntityResolver等。而在org.xml.sax.helper中则是一些方便开发人员使用的帮助类，如缺省实现所有处理器接口的帮助类DefaultHandler、方便开发人员创建XMLReader的XMLReaderFactory类等等。在这两个包中还有一些应用于SAX1的接口，同时还有几个类它们只是为了便于将在SAX1上开发的应用移植到SAX2上，在这篇文章中就不涉及了。下面是我们要关注的接口和类：</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD><B>
<H4>Package org.xml.sax</H4></B></TD>
<TD><B>
<H4>介绍</H4></B></TD></TR>
<TR>
<TD><B>Interfaces</B></TD>
<TD><B>接口</B></TD></TR>
<TR>
<TD>Attributes</TD>
<TD>定义了一个属性列表接口，供访问元素的属性列表而用。</TD></TR>
<TR>
<TD>ContentHandler</TD>
<TD>处理解析文档内容时产生的事件。</TD></TR>
<TR>
<TD>DTDHandler</TD>
<TD>处理解析DTD时的相应事件。</TD></TR>
<TR>
<TD>EntityResolver</TD>
<TD>处理外部实体。</TD></TR>
<TR>
<TD>ErrorHandler</TD>
<TD>处理解析过程中所遇到的文档错误事件。</TD></TR>
<TR>
<TD>Locator</TD>
<TD>为了定位解析中产生的内容事件在文档中的位置而准备的一个定位器接口。</TD></TR>
<TR>
<TD>XMLFilter</TD>
<TD>提供了一个方便应用开发的过滤器接口。</TD></TR>
<TR>
<TD>XMLReader</TD>
<TD>任何兼容SAX2的解析器都要实现这个接口，这个接口让应用程序可以设置或查找features和properties，注册各种事件处理器，以及开始解析文档。</TD></TR>
<TR>
<TD colSpan=2><B>Classes</B></TD></TR>
<TR>
<TD>InputSource</TD>
<TD>为XML实体准备的输入源。</TD></TR>
<TR>
<TD colSpan=2><B>Exceptions</B></TD></TR>
<TR>
<TD>SAXException</TD>
<TD>包装了一般的SAX错误和警告。 </TD></TR>
<TR>
<TD>SAXNotRecognizedException</TD>
<TD>为识别不出某些标识而抛出的异常。 </TD></TR>
<TR>
<TD>SAXNotSupportedException</TD>
<TD>为不支持某个操作而抛出的异常。</TD></TR>
<TR>
<TD>SAXParseException</TD>
<TD>包装了一个关于XML解析的错误或者警告。</TD></TR></TBODY></TABLE></P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD><B>
<H4>Package org.xml.sax.helpers</H4></B></TD>
<TD><B>
<H4>帮助类所在的包</H4></B></TD></TR>
<TR>
<TD><B>Classes</B></TD>
<TD><B>类</B></TD></TR>
<TR>
<TD>AttributesImpl</TD>
<TD>对Attributes接口的缺省实现</TD></TR>
<TR>
<TD>NamespaceSupport</TD>
<TD>提供名称空间支持。</TD></TR>
<TR>
<TD>DefaultHandler</TD>
<TD>缺省实现了四个处理器接口，方便用户开发，在开发过程中会经常用到。</TD></TR>
<TR>
<TD>LocatorImpl</TD>
<TD>提供了一个对Locator接口的实现</TD></TR>
<TR>
<TD>XMLFilterImpl</TD>
<TD>对过滤器接口的实现，使用过滤器进行应用程序开发时，继承这个类很方便。</TD></TR>
<TR>
<TD>XMLReaderFactory</TD>
<TD>为方便创建不同的XMLReader而提供。也会经常用到。</TD></TR></TBODY></TABLE></P>
<P><A name=2><SPAN class=atitle2>理解并使用SAX</SPAN></A><BR>SAX的设计实现与DOM是完全不同的！DOM处理XML文档是基于将XML文档解析成树状模型，放入内存进行处理。而SAX则是采用基于事件驱动的处理模式，它将XML文档转化成一系列的事件，由单独的事件处理器来决定如何处理。为了了解如何使用SAX API处理XML文档，这里先介绍一下SAX所使用的基于事件驱动的处理模式。</P>
<P>这种基于事件的处理模式是一种通用的程序设计模式，被广泛应用于GUI设计。在JAVA的AWT，SWING以及JAVA BEANS中就有它的身影。而SAX的基于事件驱动的处理模式就与上面三者中的非常相像。</P>
<P>基于事件的处理模式主要是围绕着事件源以及事件处理器（或者叫监听器）来工作的。一个可以产生事件的对象被称为事件源，而可以针对事件产生响应的对象就被叫做事件处理器。事件源和事件处理器是通过在事件源中的事件处理器注册方法连接的。这样当事件源产生事件后，调用事件处理器相应的处理方法，一个事件就获得了处理。当然在事件源调用事件处理器中特定方法的时候，会传递给事件处理器相应事件的状态信息，这样事件处理器才能够根据事件信息来决定自己的行为。</P>
<P>在SAX接口中，事件源是org.xml.sax包中的XMLReader，它通过parse()方法来开始解析XML文档并根据文档内容产生事件。而事件处理器则是org.xml.sax包中的ContentHandler,DTDHandler,ErrorHandler,以及EntityResolver这四个接口。它们分别处理事件源在解析过程中产生的不同种类的事件（其中DTDHandler是为解析文档DTD时而用）。而事件源XMLReader和这四个事件处理器的连接是通过在XMLReader中的相应的事件处理器注册方法set***()来完成的。详细介绍请见下表： 
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD>处理器名称</TD>
<TD>所处理事件</TD>
<TD>注册方法</TD></TR>
<TR>
<TD>org.xml.sax.ContentHandler</TD>
<TD>跟文档内容有关的所有事件： 
<OL class=n01>
<LI>文档的开始和结束 
<LI>XML元素的开始和结束 
<LI>可忽略的实体 
<LI>名称空间前缀映射开始和结束 
<LI>处理指令 
<LI>字符数据和可忽略的空格 </LI></OL></TD>
<TD>XMLReader中的setContentHandler(ContentHandler handler)方法</TD></TR>
<TR>
<TD>org.xml.sax.ErrorHandler</TD>
<TD>处理XML文档解析时产生的错误。如果一个应用程序没有注册一个错误处理器类，会发生不可预料的解析器行为。</TD>
<TD>setErrorHandler(ErrorHandler handler)</TD></TR>
<TR>
<TD>org.xml.sax.DTDHandler</TD>
<TD>处理对文档DTD进行解析时产生的相应事件</TD>
<TD>setDTDHandler(DTDHandler handler)</TD></TR>
<TR>
<TD>org.xml.sax.EntityResolver</TD>
<TD>处理外部实体</TD>
<TD>setEntityResolver(EntityResolver resolver)</TD></TR></TBODY></TABLE></P>
<P>在这四个处理器接口中，对我们最重要的是ContentHandler接口。下面让我们看一下对其中方法的说明： 
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD>方法名称</TD>
<TD>方法说明</TD></TR>
<TR>
<TD>public void <B>setDocumentLocator</B>(Locator locator)</TD>
<TD>设置一个可以定位文档内容事件发生位置的定位器对象</TD></TR>
<TR>
<TD>public void <B>startDocument</B>() throws SAXException</TD>
<TD>用于处理文档解析开始事件</TD></TR>
<TR>
<TD>public void <B>endDocument</B>() throws SAXException</TD>
<TD>用于处理文档解析结束事件</TD></TR>
<TR>
<TD>public void <B>startPrefixMapping</B>(java.lang.String prefix, java.lang.String uri) throws SAXException</TD>
<TD>用于处理前缀映射开始事件，从参数中可以得到前缀名称以及所指向的uri</TD></TR>
<TR>
<TD>public void <B>endPrefixMapping</B>(java.lang.String prefix) throws SAXException</TD>
<TD>用于处理前缀映射结束事件，从参数中可以得到前缀名称</TD></TR>
<TR>
<TD>public void <B>startElement</B>(java.lang.String namespaceURI,java.lang.String localName,java.lang.String qName,Attributes atts) throws SAXException</TD>
<TD>处理元素开始事件，从参数中可以获得元素所在名称空间的uri，元素名称，属性列表等信息</TD></TR>
<TR>
<TD>public void <B>endElement</B>(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName) throws SAXException</TD>
<TD>处理元素结束事件，从参数中可以获得元素所在名称空间的uri，元素名称等信息</TD></TR>
<TR>
<TD>public void <B>characters</B>(char[] ch, int start, int length) throws SAXException</TD>
<TD>处理元素的字符内容，从参数中可以获得内容</TD></TR>
<TR>
<TD>public void <B>ignorableWhitespace</B>(char[] ch, int start, int length) throws SAXException</TD>
<TD>处理元素的可忽略空格</TD></TR>
<TR>
<TD>public void <B>processingInstruction</B>(java.lang.String target, java.lang.String data) throws SAXException</TD>
<TD>处理解析中产生的处理指令事件</TD></TR></TBODY></TABLE></P>
<P>这里再介绍一下org.xml.sax.XMLReader中的方法，然后让我们看一个具体的例子。XMLReader是所有兼容SAX2的解析器都要实现的接口，由它的方法开始解析文档，并且调用它的注册方法来注册各种事件处理器。请看下表： 
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD>方法名称</TD>
<TD>方法介绍</TD></TR>
<TR>
<TD>public Boolean <B>getFeature</B>(java.lang.String name)throws SAXNotRecognizedException,SAXNotSupportedException</TD>
<TD>得到某个feature的值</TD></TR>
<TR>
<TD>public void <B>setFeature</B>(java.lang.String name,boolean value) throws SAXNotRecognizedException,SAXNotSupportedException</TD>
<TD>设置某个feature的值，例如，如果需要解析器支持对文档进行验证那么就这么调用本方法。myReader.setFeature(http://xml.org/sax/features/validation,true);其中myReader是XMLReader的实例。</TD></TR>
<TR>
<TD>public java.lang.Object <B>getProperty</B>(java.lang.String name)throws SAXNotRecognizedException,SAXNotSupportedException</TD>
<TD>返回一个property的值</TD></TR>
<TR>
<TD>public void <B>setProperty</B>(java.lang.String name,java.lang.Object value)throws SAXNotRecognizedException,SAXNotSupportedException</TD>
<TD>设置一个property的值</TD></TR>
<TR>
<TD>public void <B>setEntityResolver</B>(EntityResolver resolver)</TD>
<TD>注册处理外部实体的EntityResolver</TD></TR>
<TR>
<TD>public EntityResolver <B>getEntityResolver</B>()</TD>
<TD>得到系统中注册的EntityResolver</TD></TR>
<TR>
<TD>public void <B>setDTDHandler</B>(DTDHandler handler)</TD>
<TD>注册处理DTD解析事件的DTDHandler</TD></TR>
<TR>
<TD>public DTDHandler <B>getDTDHandler</B>()</TD>
<TD>得到系统中注册的DTDHandler</TD></TR>
<TR>
<TD>public void <B>setContentHandler</B>(ContentHandler handler)</TD>
<TD>注册处理XML文档内容解析事件的ContentHandler</TD></TR>
<TR>
<TD>public ContentHandler <B>getContentHandler</B>()</TD>
<TD>得到系统中注册的ContentHandler</TD></TR>
<TR>
<TD>public void <B>setErrorHandler</B>(ErrorHandler handler)</TD>
<TD>注册处理文档解析错误事件的ErrorHandler</TD></TR>
<TR>
<TD>public ErrorHandler <B>getErrorHandler</B>()</TD>
<TD>得到系统中注册的ErrorHandler</TD></TR>
<TR>
<TD>public void <B>parse</B>(InputSource input)throws java.io.IOException,SAXException</TD>
<TD>开始解析一个XML文档。</TD></TR>
<TR>
<TD>public void <B>parse</B>(java.lang.String systemId)throws java.io.IOException,SAXException</TD>
<TD>开始解析一个使用系统标识符标识的XML文档。这个方法只是上面方法的一个快捷方式它等同于：parse(new InputSource(systemId));</TD></TR></TBODY></TABLE></P>
<P><A name=3><SPAN class=atitle2>一个实例</SPAN></A><BR>让我们通过例子来看一下使用SAX解析XML文档的应用程序是如何建立的。下面是在应用程序中被处理的XML文档。为了说明SAX对名称空间的支持，我在这里特意加了一个有名称空间的元素，在这里会产生相应的前缀映射开始和结束事件。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;?xml version="1.0" encoding="GB2312"?&gt;
&lt;我的书架 &gt;
    &lt;技术书籍&gt;
        &lt;图书&gt;
            &lt;书名&gt;JAVA 2编程详解&lt;/书名&gt;
            &lt;价格 货币单位="人民币"&gt;150&lt;/价格&gt;
            &lt;购买日期&gt;2000,1,24&lt;/购买日期&gt;
        &lt;/图书&gt;      
    &lt;/技术书籍&gt;
    &lt;book:文学书籍 xmlns:book="http://javausr.com"/&gt;
    &lt;历史书籍/&gt;
&lt;/我的书架&gt;</CODE></PRE></TD></TR></TBODY></TABLE>
<P>这里的例子程序只是简单地将遇到的事件信息打印出来。我们首先实现ContentHandler接口来处理在XML文档解析过程中产生的和文档内容相关的事件，代码如下所示MyContentHandler.java：<BR>package com.javausr.saxexample; 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

public class MyContentHandler implements ContentHandler {

    private StringBuffer buf;

    public void setDocumentLocator( Locator locator ) {
    }

    public void startDocument() throws SAXException {
        buf=new StringBuffer();
        System.out.println("*******开始解析文档*******");
    }

    public void endDocument() throws SAXException {
        System.out.println("*******解析文档结束*******");
    }

    public void processingInstruction( String target, String instruction )
        throws SAXException {
    }

    public void startPrefixMapping( String prefix, String uri ) {
          System.out.println("\n前缀映射: " + prefix +" 开始!"+ "  它的URI是:" + uri);
    }

    public void endPrefixMapping( String prefix ) {
          System.out.println("\n前缀映射: "+prefix+" 结束!");
    }

    public void startElement( String namespaceURI, String localName,
                                  String fullName, Attributes attributes )
                          throws SAXException {
        System.out.println("\n 元素: " + "["+fullName+"]" +" 开始解析!");
        // 打印出属性信息
        for ( int i = 0; i &lt; attributes.getLength(); i++ ) {
            System.out.println("\t属性名称:" + attributes.getLocalName(i)
                + " 属性值:" + attributes.getValue(i));
        }
    }

    public void endElement( String namespaceURI, String localName,
                                                      String fullName )
                          throws SAXException {
        //打印出非空的元素内容并将StringBuffer清空                  
      String nullStr="";
        if (!buf.toString().trim().equals(nullStr)){
           System.out.println("\t内容是: " + buf.toString().trim());
        }
        buf.setLength(0);
        //打印元素解析结束信息
        System.out.println("元素: "+"["+fullName+"]"+" 解析结束!");              
    }

    public void characters( char[] chars, int start, int length )
                                throws SAXException {
          //将元素内容累加到StringBuffer中                
          buf.append(chars,start,length);
    }

    public void ignorableWhitespace( char[] chars, int start, int length )
                                  throws SAXException {
    }

    public void skippedEntity( String name ) throws SAXException {
    }
}</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>下面让我们创建一个调入了xerces解析器来实现XMLReader接口、并使用刚才创建的MyContentHandler来处理相应解析事件的MySAXApp.java类：<BR>package com.javausr.saxexample; 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import java.io.IOException;

public class MySAXApp {

  public static void main( String[] args ) {
    
    if ( args.length != 1 ) {
      System.out.println("输入: java MySAXApp ");
      System.exit(0);
    }

    try {
        // 初始化reader
        XMLReader reader = XMLReaderFactory.createXMLReader
                          ("org.apache.xerces.parsers.SAXParser") ;

        // 创建ContentHandler的实例
        ContentHandler contentHandler = new MyContentHandler();

        // 在reader中注册实例化的ContentHandler
        reader.setContentHandler( contentHandler );

        // 开始解析文档
        reader.parse(args[0]);

    } catch ( IOException e ) {
        System.out.println("读入文档时错: " + e.getMessage());
    } catch ( SAXException e ) {
        System.out.println("解析文档时错: " + e.getMessage());
    }
  }
}</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>下面让我们来看一下执行结果: 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
D:\sax\classes&gt;java com.javausr.saxexample.MySAXApp d:\book.xml
*******开始解析文档*******

元素: [我的书架] 开始解析!

元素: [技术书籍] 开始解析!

元素: [图书] 开始解析!

元素: [书名] 开始解析!
        内容是: JAVA 2编程详解
元素: [书名] 解析结束!

元素: [价格] 开始解析!
        属性名称:货币单位 属性值:人民币
        内容是: 150
元素: [价格] 解析结束!

元素: [购买日期] 开始解析!
        内容是: 2000,1,24
元素: [购买日期] 解析结束!
元素: [图书] 解析结束!
元素: [技术书籍] 解析结束!

前缀映射: book 开始!  它的URI是:http://javausr.com

元素: [book:文学书籍] 开始解析!
元素: [book:文学书籍] 解析结束!

前缀映射: book 结束!

元素: [历史书籍] 开始解析!
元素: [历史书籍] 解析结束!
元素: [我的书架] 解析结束!
*******解析文档结束*******</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>上面就是使用SAX解析一个XML文档的基本过程，但是MyContentHandler只是处理了解析过程中和文档内容相关的事件，如果在解析过程中出现了错误那我们需要实现ErrorHandler接口来处理。如果不注册一个错误处理器来处理的话，那么错误事件将不会被报告，而且解析器会出现不可预知的行为。在解析过程中产生的错误被分成了3类，它们分别是warning，error，以及fatalerror，也就是说在ErrorHandler中有这么三个相应的方法来处理这些错误事件。下面是对这三个错误处理方法的介绍： 
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD>方法名称</TD>
<TD>方法介绍</TD></TR>
<TR>
<TD>warning()</TD>
<TD>SAX解析器将用这个方法来报告在XML1.0规范中定义的非错误（error）或者致命错误(fatal error)的错误状态。对这个错误缺省的行为是什么也不做。SAX解析器必须在调用这个方法后继续提供正常的解析事件：应用程序应该能继续处理完文档。</TD></TR>
<TR>
<TD>error()</TD>
<TD>这个方法对应在W3C XML 1.0规范的1.2部分中定义的"error"概念。例如，一个带有有效性验证的解析器会使用这个方法来报告违反有效性验证的情况。一个带有有效性验证的解析器会使用这个方法来报告违背有些性约束的情况。缺省的行为是什么也不做。SAX解析器必须在调用这个方法后继续提供正常的解析事件：应用程序应该能继续处理完文档。如果应用程序做不到这样，则解析器即使在XML1.0规范没有要求的情况下也要报告一个致命错误。</TD></TR>
<TR>
<TD>fatalError()</TD>
<TD>这个方法对应在W3C XML1.0规范的1.2部分定义的"fatal error"概念。例如，一个解析器会使用这个方法来报告违反格式良好约束的情况。在解析器调用这个方法后应用程序必须表明这个文档是不可使用的，而且应该只是为了收集错误信息而继续进行处理（如果需要的话）：实际上，一旦在这个方法被调用后SAX解析器可以停止报告任何事件。</TD></TR></TBODY></TABLE></P>
<P>下面是实现了ErrorHandler接口的MyErrorHandler.java类：<BR>package com.javausr.saxexample; 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;
import org.xml.sax.SAXException;

public class MyErrorHandler implements ErrorHandler {

    public void warning( SAXParseException exception ) {
        System.out.println("*******WARNING******");
        System.out.println("\t行:\t" + exception.getLineNumber());
        System.out.println("\t列:\t" + exception.getColumnNumber());
        System.out.println("\t错误信息:\t" + exception.getMessage());
        System.out.println("********************");
    }

    public void error( SAXParseException exception ) throws SAXException{
        System.out.println("******* ERROR ******");
        System.out.println("\t行:\t" + exception.getLineNumber());
        System.out.println("\t列:\t" + exception.getColumnNumber());
        System.out.println("\t错误信息:\t" + exception.getMessage());
        System.out.println("********************");
    }

    public void fatalError( SAXParseException exception ) throws SAXException {
        System.out.println("******** FATAL ERROR ********");
        System.out.println("\t行:\t" + exception.getLineNumber());
        System.out.println("\t列:\t" + exception.getColumnNumber());
        System.out.println("\t错误信息:\t" + exception.getMessage());
        System.out.println("*****************************");
    }
}</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>我们也要对MySAXApp.java类做一些修改(在源代码中蓝色标出的部分)使它使用MyErrorHandler.java：<BR>package com.javausr.saxexample; 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.ContentHandler;
//引入ErrorHandler
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import java.io.IOException;

public class MySAXApp {
    
        public static void main( String[] args ) {
        
          if ( args.length != 1 ) {
            System.out.println("输入: java MySAXApp ");
            System.exit(0);
        }

        try {
            // 初始化reader
            XMLReader reader = XMLReaderFactory.createXMLReader
                               ("org.apache.xerces.parsers.SAXParser") ;

            // 创建ContentHandler的实例
            ContentHandler contentHandler = new MyContentHandler();

            // 在reader中注册实例化的ContentHandler
            reader.setContentHandler( contentHandler );

            // 创建ErrorHandler的实例
            ErrorHandler errorHandler = new MyErrorHandler();

            // 在reader中注册实例化的ErrorHandler
            reader.setErrorHandler( errorHandler );

            // 开始解析文档
            reader.parse(args[0]);

    } catch ( IOException e ) {
        System.out.println("读入文档时错: " + e.getMessage());
    } catch ( SAXException e ) {
        System.out.println("解析文档时错: " + e.getMessage());
    }
  }</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>让我们人为制造些错误来检查一下我们的错误处理器工作情况。删除元素&lt;购买日期&gt;的闭合标记，这样会产生一个fatal error，下面是执行结果：<BR>D:\sax\classes&gt;java com.javausr.saxexample.MySAXApp d:\book.xml 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
*******开始解析文档*******

元素: [我的书架] 开始解析!

元素: [技术书籍] 开始解析!

元素: [图书] 开始解析!

元素: [书名] 开始解析!

元素: [书名] 开始解析!
        内容是: JAVA 2编程详解
元素: [书名] 解析结束!

元素: [价格] 开始解析!
        属性名称:货币单位 属性值:人民币
        内容是: 150
元素: [价格] 解析结束!

元素: [购买日期] 开始解析!
******** FATAL ERROR ********
        行:     8
        列:     7
        错误信息:       The element type "购买日期" must be terminated by the matching end-tag "&lt;/购买日期&gt;".
*****************************
解析文档时错: Stopping after fatal error: The element type "购买日期" must be terminated by the matching end-tag "&lt;/购买日期&gt;".
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>现在总结一下如何书写基于SAX的应用程序。一般步骤如下： 
<OL class=n01>
<LI>实现一个或多个处理器接口(ContentHandler, ErrorHandler, DTDHandler ,or EntityResover)。 
<LI>创建一个XMLReader类的实例。 
<LI>在新的XMLReader实例中通过大量的set*****() 方法注册一个事件处理器的实例 
<LI>调用XMLReader的parse()方法来处理文档。</LI></OL>
<P></P>
<P><A name=4><SPAN class=atitle2>使用DefaultHandler</SPAN></A><BR>现在的程序是比较完整了，但还有许多可以改进的地方。首先在我们实现的MyContentHandler.java中，你会发现有很多方法实际上什么也没有做，但为了实现ContentHandler接口，不得不把它们写出来，这样很是麻烦。SAX API已经考虑到这个问题，在它的org.xml.sax.helper包中为我们提供了一个方便实现各种处理器接口的帮助类DefaultHandler。这个类缺省实现了上面提到的4个处理器接口。这样我们只需继承这个类，然后覆盖我们想要实现的事件处理方法即可。下面我们来新建一个继承了DefaultHandler的MyDefaultHandler.java类，然后把在MyContentHandler.java和MyErrorHandler.java中实现的事件处理方法照搬到MyDefaultHandler.java类中，那些没有使用的方法就不必重复了。这里是MyDefaultHandler.java：<BR>package com.javausr.saxexample; 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.io.*;

public class MyDefaultHandler extends DefaultHandler {

    private StringBuffer buf;

    public void startDocument() throws SAXException {
        buf=new StringBuffer();
        System.out.println("*******开始解析文档*******");
    }

    public void endDocument() throws SAXException {
        System.out.println("*******解析文档结束*******");
    }

    public void startPrefixMapping( String prefix, String uri ) {
System.out.println("\n前缀映射: " + prefix +" 开始!"+ "  它的URI是:"+uri);
    }

    public void endPrefixMapping( String prefix ) {
       System.out.println("\n前缀映射: "+prefix+" 结束!");
    }

    public void startElement( String namespaceURI, String localName,
                                  String fullName, Attributes attributes )
                          throws SAXException {

        System.out.println("\n元素: " + "["+fullName+"]" +" 开始解析!");

        // 打印出属性信息
        for ( int i = 0; i &lt; attributes.getLength(); i++ ) {
            System.out.println("\t属性名称:" + attributes.getLocalName(i)
                + " 属性值:" + attributes.getValue(i));
        }
    }

    public void endElement( String namespaceURI, String localName,
                                                      String fullName )
                          throws SAXException {
       //打印出非空的元素内容并将StringBuffer清空
       String nullStr="";
       if (!buf.toString().trim().equals(nullStr)){
          System.out.println("\t内容是: " + buf.toString().trim());
       }
       buf.setLength(0);
       //打印元素解析结束信息
        System.out.println("元素: "+"["+fullName+"]"+" 解析结束!");
    }

    public void characters( char[] chars, int start, int length )
                                throws SAXException {
       //将元素内容累加到StringBuffer中
       buf.append(chars,start,length);
    }

    public void warning( SAXParseException exception ) {
        System.out.println("*******WARNING******");
        System.out.println("\t行:\t" + exception.getLineNumber());
        System.out.println("\t列:\t" + exception.getColumnNumber());
        System.out.println("\t错误信息:\t" + exception.getMessage());
        System.out.println("********************");
    }

    public void error( SAXParseException exception ) throws SAXException{
        System.out.println("******* ERROR ******");
        System.out.println("\t行:\t" + exception.getLineNumber());
        System.out.println("\t列:\t" + exception.getColumnNumber());
        System.out.println("\t错误信息:\t" + exception.getMessage());
        System.out.println("********************");
    }

    public void fatalError( SAXParseException exception ) throws SAXException {
        System.out.println("******** FATAL ERROR ********");
        System.out.println("\t行:\t" + exception.getLineNumber());
        System.out.println("\t列:\t" + exception.getColumnNumber());
        System.out.println("\t错误信息:\t" + exception.getMessage());
        System.out.println("*****************************");
    }
}</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>我们也要对MySAXApp.java做相应的修改，修改已在源代码中标出：<BR>package com.javausr.saxexample; 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
//引入DefaultHandler
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.SAXException;
import java.io.IOException;

public class MySAXApp {

  public static void main( String[] args ) {
        
      if ( args.length != 1 ) {
        System.out.println("输入: java MySAXApp ");
        System.exit(0);
      }

    try {
        // 初始化reader
        XMLReader reader = XMLReaderFactory.createXMLReader
                         ("org.apache.xerces.parsers.SAXParser") ;

        // 创建DefaultHandler的实例
        DefaultHandler defaultHandler=new MyDefaultHandler();

        //在reader中将defaultHandler注册为ContentHandler
        reader.setContentHandler(defaultHandler);

        //在reader中将defaultHandler注册为ErrorHandler
        reader.setErrorHandler(defaultHandler);

        // 开始解析文档
        reader.parse(args[0]);

    } catch ( IOException e ) {
        System.out.println("读入文档时错: " + e.getMessage());
    } catch ( SAXException e ) {
        System.out.println("解析文档时错: " + e.getMessage());
    }
  }
}</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P><A name=5><SPAN class=atitle2>使用过滤器</SPAN></A><BR>在SAX API中还提供了一个过滤器接口org.xml.sax.XMLFilter，以及对它的缺省实现org.xml.sax.helper.XMLFilterImpl。使用它们可以很容易的开发出复杂的SAX应用。这里要先介绍一下过滤器设计模式。这个设计模式很好理解，就像一个净化水的过程。自然界中的水流过一个个的过滤器得到最后的饮用水。这些过滤器，有的是清除水中的泥沙，有的是杀灭水中的细菌，总之不同的过滤器完成不同的任务。在应用开发中，我们让被改造的对象（这里是事件流）通过这些过滤器对象从而得到改造后符合要求的对象。这样，在过滤器的帮助之下，我们可以非常方便的在每个过滤器中实现一个特定功能，从而创建结构复杂的应用程序。在应用程序中你可以构造任意多个过滤器，将它们串接起来完成任务。</P>
<P>在SAX API中org.xml.sax.XMLFilter接口继承了org.xml.sax.XMLReader接口。它与XMLReader不同的是它不像XMLReader那样通过解析文档来获取事件，而是从其他XMLReader中获取事件，当然这也包括从其他的XMLFilter中获取事件。在org.xml.sax.XMLFilter中有两个方法： 
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD>方法名称</TD>
<TD>方法描述</TD></TR>
<TR>
<TD>Public void setParent(XMLReader parent)</TD>
<TD>设置父XMLReader。这个方法让应用程序将这个过滤器连接到它的父XMLReader (也可能是另一个过滤器)。 </TD></TR>
<TR>
<TD>Public XMLReader getParent()</TD>
<TD>获取父XMLReader。这个方法让应用程序可以查询父XMLReader（也可能是另一个过滤器）。最好不要在父XMLReader中直接进行任何操作：让所有的事件通过这个过滤器来处理。</TD></TR></TBODY></TABLE></P>
<P>我们不需要自己实现org.xml.sax.XMLFilter接口，在SAX API 中提供了一个org.xml.sax.helper.XMLFilterImpl类，它不仅实现了org.xml.sax.XMLFilter接口而且还实现了其他四个核心处理器接口，我们只需要继承它即可完成我们的过滤器。刚开始使用XMLFilterImpl比较容易让人迷惑，你只需要记住： 
<OL class=n01>
<LI>在你继承的XMLFilterImpl类中用set****()方法这册的事件处理器是给过滤后的事件流而用的。 
<LI>在你继承的XMLFilterImpl类中实现的那些事件处理方法，比如startDocument()、startElement()、characters()等才是这个过滤器实现它自身功能的地方。而通过继承XMLFilterImpl而实现的这个类会被造型成各种处理器（它本身实现了四个处理器接口）用在它的父XMLReader中。这个步骤会在你调用自己创建的过滤器的parse()方法开始解析文档时被自动执行（请参见SAX源代码）。 
<LI>如果不是使用带参数的构造器创建XMLFilter对象，务必使用setParent(XMLReader parent)方法连接它的父XMLReader。 
<LI>如果使用多个过滤器的话，执行顺序是从父亲到最后的过滤器。但是开始解析却要调用最后一个过滤器的parse()方法。</LI></OL>
<P></P>
<P>下面让我们结合已有的例子来演示过滤器org.xml.sax.XMLFilter的作用。我们在这个过滤器中要过滤掉&lt;技术书籍&gt;这个元素，最后得到的事件流还是由上边实现的MyDefaultHandler来处理。源代码如下MyFilter.java：<BR>package com.javausr.saxexample; 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.io.*;

public class MyFilter extends XMLFilterImpl {

   private String currentElement;

   public MyFilter( XMLReader parent ) {
      super(parent);
   }

   /**
    * 过滤掉元素&lt;技术书籍&gt;的开始事件
    **/
   public void startElement( String namespaceURI, String localName,
                             String fullName, Attributes attributes )
      throws SAXException {

         currentElement = localName;
         if ( !localName.equals("技术书籍") ) {
           super.startElement(namespaceURI, localName, fullName, attributes);
         }
      }

   /**
    * 过滤掉元素&lt;技术书籍&gt;的结束事件
    **/
   public void endElement(String namespaceURI, String localName, String
                          fullName)
      throws SAXException {

         if ( !localName.equals("技术书籍") ) {
            super.endElement(namespaceURI, localName, fullName);
         }
    }

   /**
    * 过滤掉元素&lt;技术书籍&gt;中的内容
    **/
    public void characters(char[] buffer, int start, int length) 
throws SAXException {
        if ( !currentElement.equals("技术书籍") ) {
          super.characters( buffer,start,length );
        }
    }
}</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>同样我们还要修改MySAXApp.java，修改后的代码如下所示：<BR>package com.javausr.saxexample; 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.helpers.DefaultHandler;
//引入XMLFilter
import org.xml.sax.XMLFilter;
import org.xml.sax.SAXException;
import java.io.IOException;

public class MySAXApp {

  public static void main( String[] args ) {

    
    if ( args.length != 1 ) {
      System.out.println("输入: java MySAXApp ");
      System.exit(0);
    }

    try {
            // 初始化reader
        XMLReader reader = XMLReaderFactory.createXMLReader
                           ("org.apache.xerces.parsers.SAXParser") ;

        //初始化过滤器
        XMLFilter myFilter=new MyFilter(reader);

        // 创建DefaultHandler的实例
        DefaultHandler defaultHandler=new MyDefaultHandler();

        //为过滤后的事件流设置ContentHandler
        myFilter.setContentHandler(defaultHandler);

        //为过滤后的事件流设置ErrorHandler
        myFilter.setErrorHandler(defaultHandler);


            // 开始解析文档，注意是使用myFilter中的解析方法
        myFilter.parse(args[0]);

      } catch ( IOException e ) {
            System.out.println("读入文档时错: " + e.getMessage());
      } catch ( SAXException e ) {
            System.out.println("解析文档时错: " + e.getMessage());
    }
  }
}</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>这里是最后的执行结果，我们可以发现有关&lt;技术书籍&gt;的全部事件已经被过滤掉了。认真看一下结果，你一定觉得奇怪，为什么&lt;技术书籍&gt;元素的孩子元素仍然存在。请记住SAX是把XML文档解析成事件流，所有没有被过滤的事件都会保留下来。这就是SAX和DOM的最大不同。在DOM中文档被解析成了树状模型，如果你删除一个元素，那么这个元素以及它的孩子元素就都会被删除，这符合树状模型的特点。</P>
<P>D:\sax\classes&gt;java com.javausr.saxexample.MySAXApp d:\book.xml 
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
*******开始解析文档*******

元素: [我的书架] 开始解析!

元素: [图书] 开始解析!

元素: [书名] 开始解析!
        内容是: JAVA 2编程详解
元素: [书名] 解析结束!

元素: [价格] 开始解析!
        属性名称:货币单位 属性值:人民币
        内容是: 150
元素: [价格] 解析结束!

元素: [购买日期] 开始解析!
        内容是: 2000,1,24
元素: [购买日期] 解析结束!
元素: [图书] 解析结束!

前缀映射: book 开始!  它的URI是:http://javausr.com

元素: [book:文学书籍] 开始解析!
元素: [book:文学书籍] 解析结束!

前缀映射: book 结束!

元素: [历史书籍] 开始解析!
元素: [历史书籍] 解析结束!
元素: [我的书架] 解析结束!
*******解析文档结束*******</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P><A name=6><SPAN class=atitle2>一些值得注意的问题</SPAN></A><BR>首先是有关元素内容的问题，在SAX API定义中元素内容可以在一次事件（由characters()方法处理）中返回，也可以在多次事件中返回，这样我们就应该考虑不能一次得到所有内容数据的情况。一般的解决办法是定义一个StringBuffer由它来保存内容数据，在元素结束或者新元素开始的时候清空这个StringBuffer从而可以保存新的内容数据。请参考上面的相应的源代码。</P>
<P>还有在SAX API中特意提到从 <B>characters</B>(char[] ch,int start,int length)方法中提取数据时一定不要从返回的字符数组范围之外读取，这一点我们也要切记。</P>
<P>另一个值得注意的问题是，在 <B>startElement</B>()方法中返回的Attributes属性列表中的属性顺序并没有被特意规定，在不同的SAX实现中也各不相同。所以我们在编写程序时不要把属性顺序想成一定的。</P>
<P><A name=7><SPAN class=atitle2>SAX与DOM的比较</SPAN></A><BR>通过上面的介绍我想大家对SAX已经有了一个基本的了解。每一个进行XML开发的编程人员都知道DOM，那为什么在有了DOM这个功能强大的文档对象模型之后，我们还需要SAX？这就要从它们根本不同的实现方法上来分析。DOM解析器是通过将XML文档解析成树状模型并将其放入内存来完成解析工作的，而后对文档的操作都是在这个树状模型上完成的。这个在内存中的文档树将是文档实际大小的几倍。这样做的好处是结构清除、操作方便，而带来的麻烦就是极其耗费系统资源。而SAX正好克服了DOM的缺点。SAX解析器的处理过程是通读整个文档，根据文档内容产生事件，而把对这些事件的处理交由事件处理器处理。SAX不需要在内存中保存整个文档，它对系统资源的节省是显而易见的。这样在一些需要处理大型XML文档和性能要求比较高的场合就要用SAX了。</P>
<P>下面的表格列出了SAX和DOM在一些方面的对照： 
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD>SAX</TD>
<TD>DOM</TD></TR>
<TR>
<TD>顺序读入文档并产生相应事件，可以处理任何大小的XML文档</TD>
<TD>在内存中创建文档树，不适于处理大型XML文档。</TD></TR>
<TR>
<TD>只能对文档按顺序解析一遍，不支持对文档的随意访问。</TD>
<TD>可以随意访问文档树的任何部分，没有次数限制。</TD></TR>
<TR>
<TD>只能读取XML文档内容，而不能修改</TD>
<TD>可以随意修改文档树，从而修改XML文档。</TD></TR>
<TR>
<TD>开发上比较复杂，需要自己来实现事件处理器。</TD>
<TD>易于理解，易于开发。</TD></TR>
<TR>
<TD>对开发人员而言更灵活，可以用SAX创建自己的XML对象模型。</TD>
<TD>已经在DOM基础之上创建好了文档树。</TD></TR></TBODY></TABLE></P>
<P>通过对SAX和DOM的分析，它们各有自己的不同应用领域： 
<OL class=n01>
<LI>SAX适于处理下面的问题： 
<LI>对大型文档进行处理。 
<LI>只需要文档的部分内容，或者只需要从文档中得到特定信息。 
<LI>想创建自己的对象模型的时候。</LI></OL>
<P></P>
<P>DOM适于处理下面的问题： 
<OL class=n01>
<LI>需要对文档进行修改 
<LI>需要随机对文档进行访问，例如XSLT解析器。</LI></OL>
<P></P>
<P>对SAX的介绍到这里就告一段落了，希望能对大家有所帮助：），本文的绝大部分参考资料都来源于http://www.megginson.com/SAX/ 以及SAX API（虽然说SAX有了自己新的网站http://sax.sourceforge.net/ 但我从来没有成功访问过！） ，感谢David Megginson和其他SAX开发人员给我们提供了这么一个好东东。本文如有错误和不妥的地方还请大家指正。</P>
<P><A name=8><SPAN class=atitle2>附录: SAX的版权声明</SPAN></A><BR>SAX2 is Free!</P>
<P>I hereby abandon any property rights to SAX 2.0 (the Simple API for XML), and release all of the SAX 2.0 source code, compiled code, and documentation contained in this distribution into the Public Domain. SAX comes with NO WARRANTY or guarantee of fitness for any purpose.</P>
<P>David Megginson, david@megginson.com</P>
<P>2000-05-05</P><!-- Make author heading singular or plural as needed-->
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1><SPAN class=atitle2>作者简介</SPAN></A><BR>王晓强，万千程序开发者中的一员，并乐在其中。热爱java和linux，一直利用java和xml相关技术进行应用开发，并在这些方面积累了丰富经验。可通过 <A href="mailto:forff@sina.com">grekiller@yeah.net</A> 与他联系。</TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/jackybu/aggbug/2558.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-28 16:09 <a href="http://www.blogjava.net/jackybu/articles/2558.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>应该使用SAX还是DOM?</title><link>http://www.blogjava.net/jackybu/articles/2557.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Mon, 28 Mar 2005 08:04:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2557.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2557.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2557.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2557.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2557.html</trackback:ping><description><![CDATA[作者 : Nazmul Idris 译者：wanghuzi<BR>创建于 : May 22, 1999<BR>修改于 : May 23, 1999 5:57 pm<BR><BR>内容列表<BR><BR>--------------------------------------------------------------------------------<BR><BR>为什么他们同时存在<BR><BR>什么是DOM?<BR><BR>什么是SAX?<BR><BR>什么时候使用DOM<BR><BR>什么时候使用SAX<BR><BR>结论<BR><BR>为什么他们同时存在<BR><BR>--------------------------------------------------------------------------------<BR><BR>SAX (Simple API for XML) 和 DOM (Document Object Model) 都是为了让程序员不用写一个解析器就可以访问他们的资料信息。通过利用XML 1.0格式保存信息,以及使用SAX或者DOM APIs你的程序可以使用任何解析器。这是因为使用他们所喜爱的语言开发解析器的开发者必须实现SAX和DOM APIs。 SAX和DOM APIs 对多种语言中都可以实现(Java, C++, Perl, Python, 其它...)。<BR><BR>所以SAX 和 DOM都是为了同样的目的而存在,这就是使用户可以利用任何编程语言访问存入XML文档中的信息(要有一个那种编程语言的解析器)。虽然他们在提供给你访问信息的方法上大不相同。<BR><BR><BR><BR><BR>什么是DOM?<BR><BR>--------------------------------------------------------------------------------<BR><BR>DOM 可以让你以分层次对象模型来访问储存在XML文档中的信息。DOM生成一棵节点树(以XML文档的结构和信息为基础)你可以通过这棵树来访问你的信息。在XML文档中的文本信息转变成一组树的节点。请看下图：<BR><BR><IMG height=410 src="http://www.netbei.com/Article/UploadFiles/200409/20040904141042994.jpg" width=568 border=0> 
<P><BR>不管你的XML文档中的信息的类型 (不管是表格数据,或是一列items,或者只是文档), DOM在你创建一个XML文档的文档对象时创建一棵节点树。 DOM强迫你使用树状模型(就像 Swing TreeModel)去访问你的XML文档中的信息。这种模式确实不错因为XML原本就是分层次的。这也是DOM为什么可以把你的信息放到一棵树中的原因(即使信息是表格式的或者简单的列表????这里不知道该怎么翻原文是：even if the information is actually tabular or a simple list??????)。<BR><BR>上图是过分简单的，因为在DOM中,每一个元素节点实际上都有一系列的其他节点作为它的孩子。这些孩子节点可以包含文本值或者是其他元素节点。乍看起来，通过遍历访问一个元素的所有孩子节点来访问这个节点的值是没有必要的(举例来说:节点 "&lt;name&gt; Nazmul &lt;/name&gt;", Nazmul是值)。如果每个元素只有值的话，这确实是没有必要的。但是，元素可能含有文本数据或者其他元素;这是你要在DOM中做额外的工作来获取元素节点值的原因。 通常当你的文档中只有纯数据时,把所有的数据压成一个“块“放到字串中并让DOM把那个字串当成某个特定元素节点的值返回是适当的。这种方式并不适合如果在你的XML文档中的数据是个文档（比如像WORD文档或者FRAMEMAKER文档） 在文档中，元素的顺序是非常重要的。对于纯数据(像一个数据库表)元素的顺序是不要紧的。 之所以DOM保持从XML文档中读出的元素的顺序，因为它把所有的事物都当成文档来处理。 文档对像模型的叫法由此而来。<BR><BR>如果你计划用DOM做为JAVA对象模型用于你存储在XML文档中的信息，那么你不需要考虑SAX。可是如果你发现DOM不是一个可以用于处理XML文档信息的好的对象模式，那么你可能想看看SAX了。在一些必须使用自定义对象模型的案例中使用SAX是非常普遍的。说一句让事情看来有些糊涂的话，你也可以在DOM的基础之上创建自己的对象模式。面向对象真是个好东东。<BR><BR><BR><BR><BR>什么是SAX?<BR><BR>--------------------------------------------------------------------------------<BR><BR>SAX让你访问储存在XML文档中的信息，不是通过节点树，而是一系列的事件。你会问，这有什么益处？回答是，SAX选择不在XML文档上创建JAVA对象模型(像DOM做的那样)。 这样使得SAX更快, 同时使下面所述成为必要:<BR><BR>创立你自己的自定义对像模型<BR>创建一个监听SAX事件的类同时创建你自己的对象模型<BR>注意这些步骤对DOM而言是不必要的，因为DOM已经为你创建了一个对象模型(将你的信息用一棵节点树表示)。<BR><BR>在使用DOM的情况下,解析器做了绝大多数事情, 读入XML文档, 在这基础之上创建JAVA对象模型，然后给你一个对这个对象的引用(一个 Document对象)，因而你可以操作使用它。SAX被叫做Simple API for XML不是没有原因的, 她真的很简单。 SAX没有期待解析器去做这么多工作，所有SAX 要求的是解析器应该读入XML文档,同时根据所遇到的XML文档的标签发出一系列事件。你要自己写一个XML文档处理器类（XML document handler class）来处理这些事件,这意味着使所有标签事件有意义还有用你自己的对象模型创建对象。所以你要完成:<BR><BR>控制所有XML文档信息的自定义对象模型（或者源文档在这里的写法从来没有见过，或者怀疑源文档在这里有排版错误，先这么翻了）<BR>一个监听SAX事件(事件由SAX解析器读取你的XML文档时产生)的文档处理器，还有解释这些事件创建你自定义对象模型中的对象<BR>如果你的对象模型简单的话那么SAX在运行时会非常快。在这种情况下，它会比DOM快,因为它忽略了为你的信息创建一个树形对象模型的过程。从另一方面来说，你必须写一个SAX 文档处理器来解释所有的SAX事件(这会是一件很繁重的工作)。<BR><BR>什么类型的SAX事件被SAX解析器抛出了哪? 这些事件实际上是非常简单的。SAX会对每一个开始标签抛出事件,对每一个结束标签也是如此。它对#PCDATA和 CDATA 部分同样抛出事件。你的文档处理器 (对这些事件的监听器)要解释这些事件同时还要在他们基础之上创建你自定义的对象模型。 你的文档处理器必须对这些事件做出解释，同时这些事件发生的顺序是非常重要的。SAX同时也对processing instructions, DTDs, comments, 抛出事件. 但是它们在概念上是一样的, 你的解析器要解释这些事件(还有这些事件的发生顺序)以及使他们有意义。<BR><BR><BR><BR><BR><BR>什么时候使用DOM<BR><BR>--------------------------------------------------------------------------------<BR><BR>如果你的XML文档包含文档数据(例如, Framemaker documents stored in XML format), 那么DOM就是你的解决方案的最自然选择。如果你要创建一些类似于文档信息管理的系统，那么你不得不处理大量的文档数据。Datachannel RIO 产品就是这么一个例子,它可以索引和组织各种类型文档资源中的信息(例如Word和Excel 文件)。在这种情况下，DOM是非常合适程序去访问存贮在这些文档中的信息的。<BR><BR>然而,如果你主要处理的是结构化的数据(在XML中的序列化的JAVA对象the equivalent of serialized Java objects in XML)，DOM不是最好的选择。那就是SAX会比较合适的地方。<BR><BR><BR><BR><BR><BR><BR>什么时候使用SAX<BR><BR>--------------------------------------------------------------------------------<BR><BR>如果在你XML文档中的信息是机器易读的(和机器生成的)数据，那么SAX是让你可以访问这些信息的合适的API。机器易读和生成的数据类型包含像下面这些东东:<BR><BR>存成XML格式的Java对象属性<BR>用一些以文本为基础的查询语句(SQL, XQL, OQL)表示的查询<BR>由查询生成的结果集(这也许包含关系型数据库表中的数据编码成XML).<BR>这么看来机器生成的数据是你一般要在java中生成数据结构和类的信息。一个简单的例子是包含个人信息的地址簿，在上图所示。这个地址簿xml文件不像字处理器文档，它是一个包含已经被编码成文本的纯数据的XML文档。<BR><BR>当你的数据是这种样式，你要创建你自己的数据结构和类（对象模型）来管理操作以及持续保存这些数据。SAX容许你快速创建一个可以生成你的对象模型实例的处理器类。一个实例是：一个SAX文档处理器。它完成的工作有读入包含我的地址薄信息的XML文档，创建一个可以访问到这些信息的AddressBook类。SAX指南告诉你该怎么做到这些。这个地址薄XML文档包含person元素，person元素中有name和email元素。我的AddressBook对象模型包括下面的类：<BR><BR>AddressBook 类,Person对象的容器<BR>Person 类,String 型的name和email的容器<BR>这样我的“SAX 地址簿文档处理器”可以把person元素转变成Person对象了，然后把它们都存入AddressBook对象。这个文档处理器将name和email元素转变为String对象。<BR><BR><BR><BR><BR>结论<BR><BR>--------------------------------------------------------------------------------<BR><BR>你写的SAX文档处理器（SAX document handler）做了将元素映射为对象的工作。如果你的信息被结构化成可以容易创建这样的映射，你应该使用SAX API。从另一方面来说，如果你的数据更适宜用树来表示那么你应该使用DOM。<BR></P><img src ="http://www.blogjava.net/jackybu/aggbug/2557.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-28 16:04 <a href="http://www.blogjava.net/jackybu/articles/2557.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDOM使XML在Java中的操作比以往任何时候都更加容易。 </title><link>http://www.blogjava.net/jackybu/articles/2445.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Fri, 25 Mar 2005 09:11:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2445.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2445.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2445.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2445.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2445.html</trackback:ping><description><![CDATA[<P><SPAN class=bodycopy>碰巧，你在过去可能已经用过众多Java库中的某一个以操作XML数据结构。那么JDOM（Java文档对象模型，Java Document Object Model）的用途是什么？开发者又为什么需要它呢？</SPAN></P>
<P><SPAN class=bodycopy>JDOM是用于Java优化的XML数据操作的源代码开放的Java库。尽管它与万维网协会（W3C，World Wide Web Consortium）的DOM类似，但它是一种可供选择的文档对象模型，它不是在DOM基础上构建的，也不以DOM为模型。它们的主要区别是：DOM是与语言无关的，最初是用于HTML页的JavaScript操作，而JDOM专用于Java，因此它利用Java的特性，其中包括方法过载（method overloading）、集合（collection）、映像（reflection）和人们熟悉的编程风格等。对Java编程人员来说，JDOM显得更自然、更"适用"。就像Java优化的RMI（remote method invocation，远程方法调用）库要比与语言无关的CORBA（Common Object Request Broker Architecture，通用对象申请中介体系结构）更自然一样。</SPAN></P>
<P><SPAN class=bodycopy>你可以根据源代码开放的Apache类型（商业友好）的许可，在</SPAN><SPAN class=bodylink><A class=bodylink href="http://jdom.org/">jdom.org</A></SPAN>找到JDOM。它是合作设计开发的，并拥有超过3000个订阅者的邮件列表。此库已经被Sun公司的Java社区进程（JCP，Java Community Process）接受作为Java规范申请（JSR-102），并很快成为正式Java规范。</P>
<P><SPAN class=bodycopy>本系列文章将对JDOM进行技术介绍。本文提供一些关于重要类的信息。下一篇文章将介绍如何在自己的Java程序中应用JDOM。</SPAN></P>
<TABLE class=bodycopy cellPadding=5 width="35%" align=right bgColor=#f7f7e7 border=1 vspace="5" hspace="5">
<TBODY>
<TR>
<TD>
<CENTER>ORACLE XML工具 </CENTER>
<P><SPAN class=bodycopy>XML Developer Kit（XDK）是Oracle为开发人员提供的免费XML工具库。它包括可以与JDOM一起使用的XML分析器和XSLT转换引擎。您可以在Oracle XML主页</SPAN><SPAN class=bodylink><A class=bodylink href="http://www.oracle.com/xml">oracle.com/xml</A></SPAN>上找到关于这些工具的很多信息。</P>
<P><SPAN class=bodycopy></SPAN>要下载这个分析器，请查找名字为"XDK for Java"的XML Developer Kit。点击下载链接左栏中的"Software"。将该软件解压缩后，文件xalparserv2.jar中即包括该分析器。</P>
<P><SPAN class=bodycopy></SPAN>为了配置JDOM和其他软件，以便以缺省方式使用Oracle分析器的软件，需要将JAXP的javax.xml.parsers.SAXParserFactory系统属性设置为oracle.xml.jax. JXSAXParserFactory。它告诉JAXP你选择使用Oracle分析器。最简单的设置方法是采用命令行：</P>
<P><PRE>java -Djavax.xml.parsers.SAXParserFactory=
oracle.xml.jaxp.JXSAXParserFactory
</PRE>
<P></P>
<P><SPAN class=bodycopy>也可以通过编程来设置：</SPAN></P>
<P><PRE>System.setProperty("jaxax.xml.parsers
.SAXParserFactory",

"oracle.xml.jaxp.JXSAXParserFactory");
</PRE>
<P></P>
<P><SPAN class=bodycopy>除了XDK之外，Oracle还在Oracle9i数据库版本2中提供了一个本地XML知识库。Oracle9i XML数据库（XDB）是一个高性能、本地XML存储和检索的技术。它将W3C数据模型全面应用到Oracle9i数据库中，并为定位和查询XML提供了新的标准访问方法。采用XDB可以充分利用关系型数据库技术和XML技术的优点。</SPAN></P></TD></TR></TBODY></TABLE>
<P><SPAN class=parahead1>JDOM软件包结构</SPAN></P>
<P><SPAN class=bodycopy></SPAN>JDOM库包括六个软件包。第一个是org.jdom包，它包括表示XML文档以及其组件的类，这些组件有：Attribute、CDATA、Comment、DocType、Document、Element、EntityRef、Namespace、Processing Instruction和Text等。如果你对XML很熟悉，通过类的名字就可以知道它的用途了。</P>
<P><SPAN class=bodycopy></SPAN>下一个是org.jdom.input包，它包括用于构建XML文档的类。其中最主要和最重要的类是SAXBuilder。SAXBuilder通过监听输入的XML简单API（SAX，Simple API for XML）事件建立相应的文档。如果需要由一个文件或其他流（stream）建立文档，那就可以采用SAXBuilder。它采用SAX分析器读取流，并根据SAX分析器的"回调"建立文档。这一设计的一个好处就是SAX分析器越快，SAXBuilder也会越快。另一个主要输入类是DOMBuilder。DOMBuilder由DOM树构建。如果预先已经有了一个DOM树，并希望用JDOM版本的树来代替，那么采用DOMBuilder是非常方便的。</P>
<P></P>
<P><SPAN class=bodycopy></SPAN>对于这些可能的生成器来说，不存在什么限制。例如，由于Xerces有Xerces本地接口（XNI，Xerces Native Interface），可用于在比SAX较低的层次上进行操作，所以编写一个XNIBuilder以支持某些分析器知识，而不采用SAX，就可能很有意义。一个构成JDOM项目的很流行的生成器是ResultSetBuilder。它利用JDBC结果集合，生成一个XML文档，用以表示SQL结果，根据是哪种元素和哪种属性，它有多种不同的编排。</P>
<P><SPAN class=bodycopy></SPAN>org.jdom.output包中包括输出XML文档的一些类。其中最重要的类是XMLOutputter。它将文档转换为一个字节流，用于输出到文件、流和接口程序（sockets）中。XMLOutputter具有很多支持原始输出(raw output)、完美输出(pretty output)、压缩输出或其他输出的特殊配置选项。它是一个相当复杂的类。这可能是为什么DOM level2中仍然没有这种能力的原因。</P>
<P><SPAN class=bodycopy>其他输出器（outoutter）包括SAXOutputter，它可以产生基于文档内容的SAX事件。尽管它看起来有些神秘，但事实证明这个类在XSLT变换中极其有用，这是因为相对于将文档数据传递到一个引擎的字节来说，SAX事件是一种更为有效的方法。还有一个DOMOutputter输出器，它用于生成表示文档的DOM树。一个有趣的输出器是JTreeOutputter，它只有几十行代码，它可以建立表示文档的JTree。将它与ResultSetBuilder联合起来后，仅用几行代码就可以通过SQL查询得到结果的树视图。</SPAN></P>
<P><SPAN class=bodycopy>注意，与在DOM中不同，文档并不与它们的生成器捆绑在一起。这就形成了一个出色的模型，在这种模型中，你可以用一些类保持数据，另一些类构造数据，其他一些类来定制数据。可以随意进行混合和匹配。</SPAN></P>
<P><SPAN class=bodycopy></SPAN>org.jdom.transform和org.jdom.xpath包中还有支持内置的XSLT转换和XPath查找的类。</P>
<P><SPAN class=bodycopy></SPAN>最后，org.jdom.adapters包中还包括在DOM交互中对库提供帮助的类。该库的使用者永远不需要调用这一软件包中的类。这些类是随时可用的，因为每个DOM实现都为特定的引导任务生成不同的方法名称，所以适配器类将标准调用转换成分析器专用的调用。Java API for XML Processing（JAXP）为解决此问题提供了另一种方法，事实上降低了对这些类的需要，但是这些类仍然保留了下来，这是因为由于许可证的原因，并不是所有的分析器都支持JAXP，也不是任何地方都安装有JAXP。</P>
<P><SPAN class=parahead1>生成文档</SPAN></P>
<P><SPAN class=bodycopy></SPAN>文档由org.jdom.Documentclass表示，你可以从头建立一个文档：</P>
<P><PRE>// This builds: &lt;root/&gt;

Document doc = new Document(new Element("root"));
</PRE>
<P></P>
<P><SPAN class=bodycopy>也可以由文件、流、系统ID或URL建立一个文档：</SPAN></P>
<P><PRE>// This builds a document of whatever's in the given resource
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(url);
</PRE>
<P></P>
<P><SPAN class=bodycopy>将几个调用结合在一起，可以很容易地在JDOM中建立一个简单的文档：</SPAN></P>
<P><PRE>// This builds: &lt;root&gt;This is the root&lt;/root&gt;
Document doc = new Document();

Element e = new Element("root");
e.setText("This is the root");
doc.addContent(e);
</PRE>
<P></P>
<P><SPAN class=bodycopy>如果你是一位非常有才能的用户，你可能更愿意使用"方法链"，在这种方式中，多种方法是依次调用的。这种方式之所以有效是因为集合方法返回它们所作用的对象，下面是一个例子：</SPAN></P>
<P><PRE>Document doc = new Document(
  new Element("root").setText("This is the root"));
</PRE>
<P></P>
<P><SPAN class=bodycopy>为了进行对比，下面给出如何用JAXP/DOM创建相同的文档：</SPAN></P>
<P><PRE>// JAXP/DOM
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
Element root = doc.createElement("root");
Text text = doc.createText("This is the root");
root.appendChild(text);
doc.appendChild(root);
</PRE>
<P></P>
<P><SPAN class=parahead1>用SAXBuilder生成文档</SPAN></P>
<P><SPAN class=bodycopy></SPAN>如前所示，SAXBuilder提供了一种由任意面向字节的数据源来创建文档的简单机制。缺省的无参数SAXBuilder()构造器在后台利用JAXP来选择一个SAX分析器。如果你希望改变分析器，则可以设置javax.xml.parsers.SAXParserFactory系统属性，以指向你的分析器提供的SAXParser Factory实现。对于Oracle9i版本2 XML分析器，应当这样做：</P>
<P><PRE>java -Djavax.xml.parsers.SAXParserFactory=
oracle.xml.jaxp.JXSAXParserFactory YourApp
</PRE>
<P></P>
<P><SPAN class=bodycopy>对于Xerces分析器，应当这样做：</SPAN></P>
<P><PRE>java -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp
.SAXParserFactoryImpl YourApp
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN>如果没有安装JAXP，SAXBuilder缺省地指向Apache Xerces。一旦你创建了SAXBuilder实例，你就可以在生成器中设置几个属性，其中包括：</P>
<P><PRE>setValidation(boolean validate)
</PRE>
<P></P>
<TABLE cellPadding=5 width="35%" align=right bgColor=#f7f7e7 border=1 vspace="5" hspace="5">
<TBODY>
<TR>
<TD>
<CENTER>相关链接 </CENTER>
<P><SPAN class=bodycopy></SPAN>开放的源JDOM库<BR><SPAN class=bodylink><A class=bodylink href="http://jdom.org/">jdom.org</A></SPAN> </P>
<P><SPAN class=bodycopy></SPAN>Java Servlet编程（第二版），作者：Jason Hunter（O'Reilly及其联合出版机构出版，2001）<BR><SPAN class=bodylink><A class=bodylink href="http://www.oreilly.com/">www.oreilly.com</A></SPAN> </P></TD></TR></TBODY></TABLE>
<P><SPAN class=bodycopy></SPAN>这一方法告诉分析器在生成过程中是否根据Document Type Definition（DTD，文档类型定义）进行验证。缺省设置为"否"（off）。所用的DTD是文档中DocType引用的一个。根据任何其他DTD进行验证是不可能的，因为还没有分析器支持这一功能。</P>
<P><PRE>setIgnoringElementContentWhitespace(boolean ignoring)
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN>这一方法告诉分析器是否忽略元素内容中所谓的"可忽略空格（whitespace）"。按照XML1.0规范，元素内容中的空格必须由分析器保留，但当根据DTD进行验证时，分析器可能知道文档的特定部分不会支持空格，所以这一区域的任何空格都是"可忽略的"。其缺省设置为"否"（off）。如果你不希望使一个文档"往返旅行"，将输入内容以原样输出的话，那么将这一开关打开通常会使性能略微有些提高。注意，这一标志只有在验证已完成时才有效。而进行验证会导致性能下降，所以这一技巧仅在已应用验证时才有用。</P>
<P><PRE>setFeature(String name, String value)
</PRE>
<P></P>
<P><SPAN class=bodycopy>这一方法设置基础SAX分析器的一个特性。这是一个原始的"传递(pass-through)"调用，所以在应用这一方法时应非常小心，因为对特性的错误设置（如弄错名称空间）可能会中断JDOM行为。而且，依靠任何特定分析器特性都会限制可移植性。这一调用对于启用模式验证最为有用。</SPAN></P>
<P><PRE>setProperty(String name, Object value)
</PRE>
<P></P>
<P><SPAN class=bodycopy>这一方法设置基础SAX分析器的一个属性。它也是一个原始的"传递"调用，它具有同样的风险，而且对于有才能的用户同样有用，特别是对于模式验证。下面的代码结合这些方法，利用JAXP选择的分析器读取本地文件，验证功能有效，可忽略的空格都被忽略。</SPAN></P>
<P><PRE>SAXBuilder builder = new SAXBuilder();
builder.setValidation(true);
builder.setIgnoringElementContentWhitespace(true);
Document doc = builder.build(new File("/tmp/foo.xml"));
</PRE>
<P></P>
<P><SPAN class=parahead1>用XMLOutputter输出</SPAN></P>
<P><SPAN class=bodycopy></SPAN>文档可以以多种不同的格式输出，但最常见的输出格式还是字节流。在JDOM中，XMLOutputter类提供这一能力。它的缺省无参数生成器试图忠实地输出一个与内存中贮存的完全一样的文档。下面的代码向一个文件生成一个文档的原始表示。</P>
<P><PRE>// Raw output
XMLOutputter outp = new XMLOutputter();
outp.output(doc, fileStream);
</PRE>
<P></P>
<P><SPAN class=bodycopy>如果你不关心空格，那么你可以对文本块进行整理，以节省一点带宽：</SPAN></P>
<P><PRE>// Compressed output
outp.setTextTrim(true);
outp.output(doc, socketStream);
</PRE>
<P></P>
<P><SPAN class=bodycopy>如果希望文件打印得很漂亮，使其展示给人们看，则可以增加一些缩进空格，并增加一些新行：</SPAN></P>
<P><PRE>outp.setTextTrim(true);

outp.setIndent("  ");
outp.setNewlines(true);
outp.output(doc, System.out);
</PRE>
<P></P>
<P><SPAN class=bodycopy>对于已有格式化空格的文档，要打印得很漂亮，就一定要进行整理。否则，就会在已经格式的文档上添加新的格式，使打印的文档显得很难看。</SPAN></P>
<P><SPAN class=parahead1>定位元素树</SPAN></P>
<P><SPAN class=bodycopy>JDOM使得元素树的定位非常容易。为了取得根元素，可调用：</SPAN></P>
<P><PRE>Element root = doc.getRootElement();
</PRE>
<P></P>
<P><SPAN class=bodycopy>要得到它所有子元素的列表：</SPAN></P>
<P><PRE>List allChildren = root.getChildren();
</PRE>
<P></P>
<P><SPAN class=bodycopy>仅想得到具有给定名称的元素：</SPAN></P>
<P><PRE>List namedChildren = root.getChildren("name");
</PRE>
<P></P>
<P><SPAN class=bodycopy>仅想得到具有给定名称的第一个元素：</SPAN></P>
<P><PRE>Element child = root.getChild("name");
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN>getChildren()调用返回的列表是java.util.List，它是所有Java编程人员都知道的列表（List）接口的一个实现。这一列表令人感兴趣的地方在于它是活的。该列表的任何变化都会在支持的文档中立即反应出来。</P>
<P><PRE>// Remove the fourth child
allChildren.remove(3);
// Remove children named "jack"
allChildren.removeAll(root.getChildren("jack"));
// Add a new child, at the tail or at the head
allChildren.add(new Element("jane"));
allChildren.add(0, new Element("jill"));
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN>利用该List隐喻（List metaphor）可以不必增加过多的方法而进行许多元素操作。但是为了方便起见，在结尾增加元素或者删去给定名字的元素之类的常用任务都有涉及到元素（Element）自身的方法，而不需要首先得到该List：</P>
<P><PRE>root.removeChildren("jill");
root.addContent(new Element("jenny"));
</PRE>
<P></P>
<P><SPAN class=bodycopy>采用JDOM的一个好处就是它可以很容易地在一个文档内或在文档之间移动元素。两种情况的代码相同，如下所示：</SPAN></P>
<P><PRE>Element movable = new Element("movable");
parent1.addContent(movable);    // place
parent1.removeContent(movable); // remove
parent2.addContent(movable);    // add
</PRE>
<P></P>
<P><SPAN class=bodycopy>采用DOM时，移动元素就没有这么容易，因为在DOM中，元素是与它的生成工具紧紧联系在一起的。所以在文档之间移动DOM元素时，必须将其"导入"。</SPAN></P>
<P><SPAN class=bodycopy></SPAN>而采用JDOM时，你需要记住的唯一一件事件就是在将一个元素增加到其他位置之前，需要先先将它删除，这样你就不会在树中形成循环。detach()方法可以用一行代码完成分离/增加：</P>
<P><PRE>parent3.addContent(movable.detach());
</PRE>
<P></P>
<P><SPAN class=bodycopy>如果在将一个元素增加到另一个父元素之前忘了将它分离，则该库会产生一个异常（带有一个真正准确而有用的错误信息）。该库还会检查元素的名字和内容，以确保它们不包括空格之类的不恰当字符。它还会验证其他一些规则，如只有一个根元素、名称空间的声明是一致的、在注释和CDATA部分没有禁止使用的字符串等等。这一特性能够尽可能早地在该进程中进行"格式正确性"（well-formedness）错误检查。</SPAN></P>
<P><SPAN class=parahead1>处理元素的属性</SPAN></P>
<P><SPAN class=bodycopy>元素属性的格式如下所示：</SPAN></P>
<P><PRE>&lt;table width="100%" border="0"&gt; ... &lt;/table&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>利用对元素的引用，可以要求元素的任意给定属性值：</SPAN></P>
<P><PRE>String val = table.getAttributeValue("width");
</PRE>
<P></P>
<P><SPAN class=bodycopy>也可以将属性看作一个对象，用于进行一些特殊的操作，如类型变换等：</SPAN></P>
<P><PRE>Attribute border = table.getAttribute("border");
int size = border.getIntValue();
</PRE>
<P></P>
<P><SPAN class=bodycopy>要设置或改变一个属性，可以采用setAttribute()：</SPAN></P>
<P><PRE>table.setAttribute("vspace", "0");
</PRE>
<P></P>
<P><SPAN class=bodycopy>要删除一个属性，可以采用removeAttribute()：</SPAN></P>
<P><PRE>table.removeAttribute("vspace");
</PRE>
<P></P>
<P><SPAN class=bodycopy>处理元素文本内容</SPAN></P>
<P><SPAN class=bodycopy>一个文本内容类似于：</SPAN></P>
<P><PRE>&lt;description&gt;
  A cool demo
&lt;/description&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>在JDOM中，这一文本可以通过调用直接获得：</SPAN></P>
<P><PRE>String desc = description.getText();
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN>要记住，因为XML 1.0规范要求保留空格，所以它会返回"\n A cool demo\n"。当然，有经验的编程人员常常不希望得到格式化的空格。有一个很方便的方法可以检索文本而同时忽略其中的空格：</P>
<P><PRE>String betterDesc = description.getTextTrim();
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN>如果你真地希望去掉空格，那么有一个getTextNormalize()方法可以将内部空白成为一个标准空格。这一方法对于类似于下面这样的文本是非常方便的：</P>
<P><PRE>&lt;description&gt;
  Sometimes you have text content with formatting
  space within the string.
&lt;/description&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN>要改变文本内容，可以应用setText()方法：</P>
<P><PRE>description.setText("A new description");
</PRE>
<P></P>
<P><SPAN class=bodycopy>这一文本内的任何特殊字符都可以正确地被解释为一个字符,并根据需要在输出时删除,使以保持文本的语法正确。比如说你进行了这样一个调用：</SPAN></P>
<P><PRE>element.setText("&lt;xml/&gt; content");
</PRE>
<P></P>
<P><SPAN class=bodycopy>在内部存储中，仍然将这些字符串看作字符。不会对其内容进行隐式分析。在输出中，你将看到：</SPAN></P>
<P><PRE><ELT>&amp;lt;xml/&amp;gt; content&lt;elt&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN>这一操作保留了前面setText()调用的语义。如果你希望在一个元素中保留XML内容，则必须增加相应的JDOM子元素对象。</P>
<P><SPAN class=bodycopy></SPAN>处理CDATA节也可能在JDOM内进行。一个CDATA节指出不应被分析的文本块。它实质上是一个"语法糖块（syntactic sugar）"，它允许很容易地包含HTML或XML内容，而不需要很多&amp;lt;和&amp;gt;换码字符。要建立一个CDATA节，只需要在CDATA对象内封装字符串即可：</P>
<P><PRE>element.addContent(new CDATA("&lt;xml/&gt; content"));
</PRE>
<P></P>
<P><SPAN class=bodycopy>JDOM最了不起的地方是getText()调用返回字符串时，不会麻烦调用程序去判断它是否由CDATA节表示。</SPAN></P>
<P><SPAN class=parahead1>处理混合内容</SPAN></P>
<P><SPAN class=bodycopy>一些元素包括有很多元素，如空格、注释、文本、子元素，以及其他元素：</SPAN></P>
<P><PRE>&lt;table&gt;
  &lt;!-- Some comment --&gt;
  Some text
  &lt;tr&gt;Some child element&lt;/tr&gt;
&lt;/table&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>如果一个元素中同时包含文本和子元素，就说它包含有"混合内容" 。处理混合内容可能是非常困难的，但JDOM使它变得非常容易。标准使用情况--返回文本内容和定位子元素--都非常简单：</SPAN></P>
<P><PRE>String text = table.getTextTrim();  // "Some text"

Element tr = table.getChild("tr");  // A straight reference
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN>对于需要注释、空格块、处理指令和实体引用这样一些更复杂的应用来说，原始混合内容可以作为一个List（列表）来提供：</P>
<P><PRE>List mixedCo = table.getContent();
Iterator itr = mixedCo.iterator();
while (itr.hasNext()) {
  Object o = i.next();
  if (o instanceof Comment) {
    ...
  }
  // Types include Comment, Element, CDATA, DocType,
  // ProcessingInstruction, EntityRef, and Text
}
</PRE>
<P></P>
<P><SPAN class=bodycopy>就像子元素列表一样，对原始内容列表的改变会影响到支持的文档：</SPAN></P>
<P><PRE>// Remove the Comment.  It's "1" because "0" is a whitespace block.
mixedCo.remove(1);
</PRE>
<P></P>
<P><SPAN class=bodycopy></SPAN><SPAN class=bodycopy>如果你的观察力很强，则你会注意到这里有一个Text类。JDOM在内部采用Text类存储串内容，从而允许串具有"父辈（parentage）"关系，并更容易支持XPath访问。作为一个编程人员，在检索或设置文本时，不需要担心这种类，而只需要在访问原始内容列表时关心它就行了。. </SPAN></P>
<P><SPAN class=bodycopy></SPAN>对于DocType、处理指令和EntityRef类的详细内容，请参见jdom.org上的API文档。</P>
<P><SPAN class=parahead1>关于第2部分</SPAN></P>
<P><SPAN class=bodycopy></SPAN>在本文中，开始研究了如何在应用程序中应用JDOM。在下一篇文章中，我将研究XML Namespace、ResultSetBuilder、XSLT和XPath。你现在可以在otn.oracle.com/oraclemagazine中找到这一系列文章的第二部分。</P>
<P><SPAN class=italicbodycopy><B>Jason Hunter</B> (</SPAN><SPAN class=bodylinkitali><A class=bodylinkitali href="mailto:jasonhunter@servlets.com">jasonhunter@servlets.com</A></SPAN><SPAN class=italicbodycopy>) 是一位顾问、Servlets.com的发行人，Apache软件基金会的副总裁。他还是JCP执行委员会的委员</SPAN></P><img src ="http://www.blogjava.net/jackybu/aggbug/2445.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-25 17:11 <a href="http://www.blogjava.net/jackybu/articles/2445.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从 XML 到 Java 代码的数据绑定，系列之一（共四部分） </title><link>http://www.blogjava.net/jackybu/articles/2392.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 22:10:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2392.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2392.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2392.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2392.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2392.html</trackback:ping><description><![CDATA[<P>Brett McLaughlin <BR>Enhydra 战略家，Lutris 科技公司 <BR>2000 年 7 月</P><!-- END title and author lines --><!-- Begin Table of Contents-->
<P>
<TABLE cellSpacing=0 cellPadding=0 width=120 align=right border=0>
<TBODY>
<TR>
<TD align=middle bgColor=#cc6633><B><FONT color=#ffffff size=-1>内容：</FONT></B></TD></TR>
<TR>
<TD width="100%" bgColor=#000000><FONT size=-3><IMG height=1 alt="" src="http://www.fesge.com/wzjx/0305/jxml1.files/c.gif" width=1 border=0></FONT></TD></TR>
<TR>
<TD width="100%" bgColor=#ffffff><FONT size=-3><IMG height=4 alt="" src="http://www.fesge.com/wzjx/0305/jxml1.files/c.gif" width=1 border=0></FONT></TD></TR>
<TR>
<TD><FONT face=helvetica,helv,arial size=-2>&nbsp;<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#1"></A></FONT><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#1"><FONT size=-2>分析各种选择</FONT></A></TD></TR>
<TR>
<TD><FONT face=helvetica,helv,arial size=-2>&nbsp;<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#2"></A></FONT><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#2"><FONT size=-2>约束数据</FONT></A></TD></TR>
<TR>
<TD><FONT face=helvetica,helv,arial size=-2>&nbsp;<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#3"></A></FONT><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#3"><FONT size=-2>利用约束</FONT></A></TD></TR>
<TR>
<TD><FONT face=helvetica,helv,arial size=-2>&nbsp;<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#4"></FONT><FONT size=-2>来自 </FONT><FONT face=helvetica,helv,arial size=-2>XML </FONT><FONT size=-2>的 </FONT><FONT face=helvetica,helv,arial size=-2>Java</FONT></A></TD></TR>
<TR>
<TD><FONT face=helvetica,helv,arial size=-2>&nbsp;<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#5"></A></FONT><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#5"><FONT size=-2>总结</FONT></A></TD></TR>
<TR>
<TD><FONT face=helvetica,helv,arial size=-2>&nbsp;<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#resources"></A></FONT><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#resources"><FONT size=-2>参考资料</FONT></A></TD></TR>
<TR>
<TD><FONT face=helvetica,helv,arial size=-2>&nbsp;<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#author"></A></FONT><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#author"><FONT size=-2>作者简介</FONT></A></TD></TR>
<TR>
<TD bgColor=#000000><FONT size=-3><IMG height=3 src="http://www.fesge.com/wzjx/0305/jxml1.files/c.gif" width=137></FONT></TD></TR></TBODY></TABLE><!-- End Table of Contents -->
<P>
<BLOCKQUOTE>在这个由四部分组成的系列文章的第一部分，我们将弄清什么是数据绑定，与在 Java 应用程序中处理 XML 数据的其它方法相比它有什么优势，以及如何开始使用它。这一部分将考查为什么使用数据绑定，以及如何为各种约束建立模型，使 XML 文档能转换成 Java 对象。同时还涵盖用于生成数据绑定类的输入和输出。</BLOCKQUOTE>
<P></P>
<P>您希望在您的 Java 应用程序中使用 XML 吗？那么好，同成千上万的其他人一起上这条船吧。当您深入了解 XML 以后，也许您会发现 DOM 和 SAX API（请参阅<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#resources">参考资料</A>）不过是唬人的东西。您可能认为<I>肯定</I>存在某种简单方法可以取得 XML 文档，并通过 Java 应用程序访问它，对吗？不必通过回调或复杂的树状结构，而是使用像 <CODE>setOwner(Stringowner)</CODE> 和 <CODE>int getNumOrders()</CODE> 这样的方法，对吗？如果您曾经沿着这一思路考虑问题，那么数据绑定就是您要寻找的解决方案。</P><A name=1>
<P><STRONG class=subhead>分析各种选择</STRONG> <BR>当今各种 XML 和 XML 主义正泛滥成灾（XSL、RDF、命名空间、RSS、XML Schema、XSLT...），您可能认为现在会有很多方法去访问 Java 应用程序中的 XML 数据。令人惊讶的是，如果您寻根究底，实际只存在三种访问 XML 数据的方法。没错 -- 只有三种方法，其中的一种还是最近随一种新的 Java API 才出现的。</P>
<P>应该这样来看待这一问题：选择范围小使您更易于选出适合于您的方法。</P>
<P><B>回调</B> <BR>回调是作为一种事件驱动模型工作的。当分析 XML 文档时，某些事件 -- 如文档的起始和某个元素中的字符数据的起始 -- 将触发回调方法。通过使用执行逻辑所需的数据，您可以实现这些事件的 Java 代码。要弄清这种方法不能全靠直觉；开发人员通常要花费一段时间来理解和掌握回调模型的使用。SAX，用于 XML 的一种简单 API，是这种 XML 使用方法的事实上的标准。</P>
<P><B>树</B> <BR>更常见、更流行的是这种 API，它们取得一个 XML 文档，然后创建数据的树状结构。XML 文档成为树首，充当一种容器。它有若干子级，如根元素。根元素又有其附加的子级，依此类推，直到（在某种意义上）获得 XML 数据的一幅图为止。因为几乎每个大学生在某个阶段肯定都处理过树状结构，所以这就可用作表示 XML 数据的一种非常直观的方法。</P>
<P>用于 XML 文档树状表示的最流行的 API 就是 W3C 的推荐标准，即文档对象模型 (DOM)。一种更新的 API，JDOM （这不是首字母缩写词）最近也正一直在推广并流行开来。（虽然这个方案是我和 Jason Hunter 建立的，但我还得说实话。）另外，DOM 和 JDOM 都是 Spinnaker 方案设计的基本要求，Spinnaker 是一种新的 XML 分析器，它作为 Apache XML 方案的一部分正在开发之中。</P>
<P>虽然树状 API 看起来比事件驱动的 SAX 更易于使用，但它们并不总是合适的。非常大的文档可能需要大量的内存（尤其是使用 DOM 时）；当对树结构执行转换 (XSLT) 时，系统可能停止运转甚至彻底崩溃。虽然更新的 API（如 JDOM）能处理这些问题，但如果您必须处理极大量的数据，它们仍将是一个问题。并且，有时开发人员宁愿将 XML 文档中的数据建模为一个简单的带有值的读写方法的 Java 对象，而不用树状模型工作。例如，开发人员会宁愿不去访问名为 <CODE>skuNumber</CODE> 的子节点并设置该节点的文本值，而只想调用 <CODE>setSkuNumber("mySKU")</CODE> 并继续进行。</P><!-- SIDEBAR -->
<TABLE class=sidebar cellPadding=3 width="35%" align=right border=1>
<TBODY>
<TR>
<TD></A><A name=jargon></A><B>术语解释 </B>
<P><B>数据绑定。</B>从 Java 应用程序内部访问 XML 数据的一种新方法，使用仍在开发中的一种 API，JSR-031。</P>
<P><B>JSR-031。</B> Sun 仍在开发中的一种新的 Java 规范请求，设计用于将 XML 文档编译成一个或多个 Java 类，而在 Java 应用程序中可以方便地使用这些 Java 类。</P>
<P><B>打包<A name=Marshalling></A>。</B>将 Java 对象转换为 XML 表示，拥有当前值。</P>
<P><B>解包<A name=Unmarshalling></A>。</B>根据 XML 对象创建 Java 对象，通常是根据打包生成一个 Java 对象。</P>
<UL></UL></TD></TR></TBODY></TABLE><!-- SIDEBAR -->
<P><B>数据绑定</B> <BR>用 Java 代码访问 XML 数据的最新方法要依赖于一套新的 Java 方法和相关的 API，这些 API 仍在开发之中。数据绑定是由 Sun 构建的一种“Java 规范要求”（JSR-031，见<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#resources">参考资料</A>），它设计用于使 Java 对象<I>绑定</I>到 XML 文档更加方便，这样就使一种格式能够容易地转换为另一种格式，反之亦然。绑定引用一个具有读写方法的 Java 对象，读写方法都会影响到底层的 XML 文档，并且也都直接映射为 XML 文档中的元素及特征的名称。当您进入到本系列文章下一部分中的某些细节时，这一说明会更有意义，但在目前，只说一点就够了：这样做使 XML 文档特征 <CODE>name</CODE> 能够通过一个称为 <CODE>setName()</CODE> 的方法，来更改它的值，就像我上面暗示的那样。</P>
<P>这种访问方式正在得到普及，并且当在 XML 文档中存储配置信息时特别有用。许多开发人员发现，它非常便于直接访问所需的参数，而无须使用更复杂的树状结构。虽然这种访问对于文档转换或消息传送没有什么用处，但它对于简单数据处理是极其方便的。它是我们在本文及本系列文章中关注的第三种使用 XML 的方法。</P>
<P>（当然，任何方法随后都会引出一系列新的术语，所以请查看<A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#jargon">术语解释</A>以了解这些新的行话。）</P>
<P>是否任何 XML 文档都可以转换为 Java 对象？还是仅有某些类型的 XML 文档才可以？问得好！您很可能只希望将满足一组约束条件的文档转换为 Java 对象。这与定义 Java 接口的方法类似：您确保只实例化和使用适应该接口的对象，允许就如何操作该对象作出假定。同样，您只允许将满足一组约束条件的 XML 对象转换成 Java 对象；这使您能够按希望的方式来使用所创建的对象。</P><A name=2>
<P><STRONG class=subhead>约束数据</STRONG> <BR>在研究代码之前，您需要回答几个有关如何表示 XML 数据的问题。这是数据绑定的最具挑战性的方面之一。是为每个文档创建一个新类，还是创建某个现有类的一个实例？您要使用哪个现有类？并且最重要的是，您正在处理的文档是否适宜转换为 Java 对象？</P>
<P>那是一大堆问题，但您将在这里找到全部答案。将这些问题看作一系列决策点，一系列选择。首先，您必须确定您能否从该 XML 文档创建 Java 对象（如前面所讨论的那样）。如果能，您就要决定转换应该以新 Java 类的形式出现，还是仅以现有类的一个实例的形式出现。最后，如果选择了现有类，那么使用哪个类呢？结果就是各种各样的决策树。</P>
<P>如果我们考察清单 1 中所示的一个示例 XML 文档，然后再来处理这些问题，则决策树的意义就更加清楚了。此示例文档表示 Enhydra Application Server 中某个服务（具体说就是一个 web 容器）的配置。</P>
<P></A><A name="Listing 1">
<P><B>清单 1. 一个用于配置 Enhydra 中的 web 容器的 XML 文档</B> 
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;?xml version="1.0"?&gt;

&lt;webServiceConfiguration&nbsp;
&nbsp;&nbsp;&nbsp; version="1.0"
&nbsp;&nbsp;&nbsp; name="My Web Container"
&gt;

&nbsp; &lt;port number="80"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protocol="http"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected="false"
&nbsp; /&gt;

&nbsp; &lt;document root="/usr/local/enhydra/html"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; index="*.html,*.xml"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error="error.html"
&nbsp; /&gt;&nbsp;

&lt;/webServiceConfiguration&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</PRE></TD></TR></TBODY></TABLE>
<P>此配置文档包含有关服务本身的版本和名称的信息，以及几个嵌套的项目，每个项目都表示有关该 web 容器服务的一些附加信息。它给出了有关端口的详细信息（包括端口号、协议和安全性），也给出了文档服务信息（包括文档根、用于索引页的默认扩展名以及错误页）。所有这些合在一起，就是配置一个新的 web 容器服务所需的全部信息。</P>
<P>记住这个示例，您就可以开始回答数据表示的各个问题了。</P>
<P><B>是否适合转换？</B> <BR>绝对适合！只要看一看</A><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#Listing 1">清单 1</A> 中的 XML 文档就会发现，它表示一个对象（总体配置对象），具有若干特征或变量。其中某些变量又是另外的对象（端口和文档），这些对象又具有它们自己的特征。实际上，这是适合转换为 Java 对象的 XML 文档的一个极好例子。为了进一步保证此对象是可用的，稍后我将向您展示一种方法来约束文档中的数据。但是，还是先让我们继续沿着决策树往下走。</P>
<P><B>转换成类还是实例？</B> <BR><!--7/17 author replaced the following 3 paragraphs for clarity. 
With the suitability question laid to rest, it's time to determine
whether the XML configuration document should become a new Java class or
a new instance of an existing class. It's a rather simple issue of reusability.
In the example, probably you would have multiple web containers running
across multiple systems (a common load-balancing technique), so it becomes
obvious to use one single class throughout the application. I'll call it
<code>WebServiceConfiguration</code>.
Each XML document contains specific information about the variables of
that class, then, and should be <a href="#Unmarshalling">unmarshalled</a>
into an instance of that class.</p>

<p>If, on the other hand, you were actually to create a new class (never
mind the complexity of that task at run time), each web container could
potentially be unmarshalled into a <i>different</i> class, making maintenance
and coding of web clients nearly impossible!</p>

<p>So for our purposes, it is fairly simple to see that you need to somehow
create a Java class (<code>WebServiceConfiguration</code>).
Then when the document is unmarshalled, a new instance of the class is
created and populated with the specific data within the XML document.</p>-->解决适宜性问题以后，现在就可以作出决定，是将每个 XML 配置文档都变为一个全新的 Java 类呢，还是简单地将其变为某个现有类的一个新实例。换句话说，就是到底应该生成新代码，还是利用现有的代码。照这样来看，这就变成了一个简单的可重用性问题。更容易且更明智的做法是，为每个 XML 文档生成现有类的新实例。如果您<I>一定要</I>尝试一下从每个文档创建一个新的 Java 类，则得到的各个类之间可能没有兼容性 -- 即两个完全相同的文档可能导致两个不同的 Java 类！</P>
<P>不用这个可能引起混乱的方法，您可以采用一组 XML 约束条件（由一个 DTD 或 XML 方案表示，将在下面讲述），并根据这些约束条件来生成一个 Java 类（或多个类，根据需要）。这个生成的类将表示符合这些约束条件的任何 XML 文档；这些 XML 文档中的每一个都将被解包到生成的类的一个实例中。在这种情况下，就可以为表示 web 服务配置的文档定义约束条件。这些约束条件将被映射为一个 Java 类，我们将称之为 <CODE>WebServiceConfiguration</CODE>。然后您就可以获得任何一种表示特定 web 服务配置的 XML 文档，并假定此文档符合我们的约束条件，由它而创建出前面生成的类的一个实例。这将允许应用程序将不同的 XML 文档用作相同类型的对象，只要这些文档中的数据对于该对象设计时要达到目的来说是有效的即可。</P>
<P><B>新类还是现有的类？</B> <BR>现在您也已经有条件回答下一个问题了：您希望创建一个现有类即 <CODE>WebServiceConfiguration</CODE> 类的一个实例。剩下需要弄清的全部事情是，这个类是如何预先生成的。所以，现在请将您的注意力集中在这样一个问题上：如何获得一组约束条件，用 XML 实现它们，并保证文档符合这些约束？再一个问题就是，您如何再从这些约束条件生成一个可重用的 Java 类？</P><A name=3>
<P><STRONG class=subhead>利用文档约束条件</STRONG> <BR>既然您知道此文档将转换成一个 Java 实例，这就产生了另一个问题：要考虑到必须以某种方式保证可将此文档正确地解包到一个选定的 Java 类中。缺少变量或数据类型不正确都可能导致在解包过程中出错 -- 或者甚至在客户机访问配置错误的容器时出现运行时异常。</P>
<P>最好的情况是，在实际的解包过程开始之前，文档的作者能够保证，配置文档对于他们选择用来表示数据的类是“合法的”。阅读到这一方案的 XML 人士说不定就会转动他们的眼睛并嘀咕说，“好吧，当然您将使用 XML 文档约束条件。”确认数据对选定类的合法性可以通过引用 DTD （文档类型定义）或 XML 方案来完成。</P>
<P>通过使用一组用外部 DTD 或方案文件表示的约束条件，文档作者就可以在这些数据的“接口”上测试配置数据。换句话说，您可以这样来建立应用程序，使之能够对照所需的数据来检查包含在 XML 实例文档中的数据，而所需数据则是在文档约束条件的外部文件中指定的。这样，您就可以为数据创建一个接口。</P>
<P>在使用 DTD 方案还是使用 XML 方案之间作出决策是一种相当简单的选择：因为 Java 语言是高度类型化的，所以我们希望能在 XML 文档中支持类型化。例如，数据接口应该能够验证，为 web 容器的端口号提供的是整数，而不是字符串，后者在服务启动时将引起错误。DTD 不支持类型检查，所以我们无疑应该选择 XML 方案。虽然 XML 方案在规范的领域在有一点不确定性，但它在很大程度上已趋于稳定，并可望在年内定型。我们可以在一个 XML 方案中编写数据约束条件，然后用这个方案验证可能的实例文档，以确保解包能在这些实例文档上进行。下面的 XML 方案表示我们的 web 容器服务的数据接口。</P>
<P></A><A name="Listing 2">
<P><B>清单 2. 表示一个 web 容器配置文档的数据接口的 XML 方案</B> 
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;?xml version="1.0"?&gt;

&lt;schema targetNamespace="http://www.enhydra.org"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns="http://www.w3.org/1999/xmlSchema"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:enhydra="http://www.enhydra.org"
&gt;

&nbsp; &lt;complexType name="ServiceConfiguration"&gt;
&nbsp;&nbsp;&nbsp; &lt;attribute name="name" type="string" /&gt;
&nbsp;&nbsp;&nbsp; &lt;attribute name="version" type="float" /&gt;
&nbsp; &lt;/complexType&gt;


&nbsp; &lt;element name="serviceConfiguration" type="ServiceConfiguration" /&gt;

&nbsp; &lt;complexType name="WebServiceConfiguration"&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; baseType="ServiceConfiguration"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; derivedBy="extension"&gt;
&nbsp;&nbsp;&nbsp; &lt;element name="port"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;complexType&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;attribute name="protocol" type="string" /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;attribute name="number" type="integer" /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;attribute name="protected" type="string" /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/complexType&gt;
&nbsp;&nbsp;&nbsp; &lt;/element&gt;

&nbsp;&nbsp;&nbsp; &lt;element name="document"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;complexType&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;attribute name="root" type="string" /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;attribute name="index" type="string" /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;attribute name="error" type="string" /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/complexType&gt;
&nbsp;&nbsp;&nbsp; &lt;/element&gt;
&nbsp; &lt;/complexType&gt;

&nbsp; &lt;element name="webServiceConfiguration" type="WebServiceConfiguration" /&gt;

&lt;/schema&gt;</PRE></TD></TR></TBODY></TABLE>
<P></A><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#Listing 2">清单 2</A> 中的 XML 方案定义了几个不同的数据对象，它们合起来表示一个 web 服务的配置对象。首先，定义了一个核心服务配置（<CODE>serviceConfiguration</CODE>），它包含版本和名称。这可用作所有服务（如负载均衡服务、EJB 容器，当然还有我们的 web 服务）的基本对象。然后，作为此基本服务的扩展，又定义了 web 服务配置（<CODE>webServiceConfiguration</CODE>）。请注意，Java 成型之后，方案就已经为数据接口建立了模型。我们将附加的 web 服务属性 <CODE>port</CODE> 和 <CODE>document</CODE> 添加到 <CODE>version</CODE> 和 <CODE>name</CODE> 基本属性中。这些属性本身都是对象，具有它们自己的属性（<CODE>protocol</CODE>、<CODE>root</CODE>、<CODE>error</CODE> 等）。</P>
<P>在此方案的定义方式中，特征代表简单的 Java 类型，通常是原始 (primitive) 类型。这样，<CODE>name</CODE> 和 <CODE>version</CODE> 就分别成为类型 <CODE>String</CODE> 和 <CODE>float</CODE> 的 Java 原始类型。<CODE>port</CODE> 和 <CODE>document</CODE> 这样的元素成为 Java 对象，它们可以有自己的属性，还是用特征来表示。这样就出现了递归现象：元素变成新对象，并对它的每个属性进行检查。如果属性是一个特征，就为此对象创建一个简单的 Java 原始成员变量；如果属性是元素，则创建一个新的对象，并作为一个成员变量将其添加，然后在这个新对象上又开始同样的过程，直到全部类都已创建为止。</P><A name=4>
<P><STRONG class=subhead>从萝卜 ... 嗯 ... XML 获得 Java</STRONG> <BR>一旦创建了 XML 方案，您就需要从这个方案中提取出必需的信息，来确定应该创建哪些 Java 类。这个过程的第一步是查看 XML 方案，并严格确定输出应该是什么。对于简单的 <CODE>serviceConfiguration</CODE> 对象，定义了两个 Java 原始属性：<CODE>name</CODE> 和 <CODE>version</CODE>。对于这样一个简单的对象，确定所需的接口并不难。只需将被定义类型的名称的首字母大写，并将这些 Java 属性添加到接口中即可，如清单 3 所示。</P>
<P></A><A name="Listing 3">
<P><B>清单 3. 为 <CODE>ServiceConfiguration</CODE> 接口而从 XML 方案生成的 Java 代码</B> 
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>public interface ServiceConfiguration {
&nbsp;&nbsp;&nbsp; public void setVersion(float version);
&nbsp;&nbsp;&nbsp; public float getVersion();
&nbsp;&nbsp;&nbsp; public void setName(String name);
&nbsp;&nbsp;&nbsp; public String getName();
}</PRE></TD></TR></TBODY></TABLE>
<P>这是相当明白易懂的；</A><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/index.shtml#Listing 3">清单 3</A> 中的接口为 XML 方案中定义的属性提供读方法和写方法。另外，您将需要生成一个实现类来定义此接口的各个成员变量，并实现此接口中的每个方法。这种使接口从实现中分离出来的方法使我们能够为特定的需要提中供多种实现。 例如，某个特定的服务可能需要执行计算，而不只是接受从写方法中收到的值。现在考虑那种更复杂的情况还有点为时尚早，但我将在后续文章中重新提到它。然而，一般说来，您仍可以确定实现类应该像什么样子，如清单 4 所示。</P>
<P><A name="Listing 4">
<P><B>清单 4. 为 <CODE>ServiceConfiguration</CODE> 实现而从 XML 方案生成的 Java 代码</B> 
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>public class ServiceConfigurationImpl implements ServiceConfiguration {
&nbsp;&nbsp;&nbsp; private String name;
&nbsp;&nbsp;&nbsp; private float version;

&nbsp;&nbsp;&nbsp; public void setVersion(float version) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.version = version;
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; public float getVersion() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return version;
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; public void setName(String name) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.name = name;
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; public String getName() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return name;
&nbsp;&nbsp;&nbsp; }
}</PRE></TD></TR></TBODY></TABLE>
<P>相同的原则也适用于 XML 方案中定义的其它对象。您可以在下面查看到其它 Java 类（因为它们都是应该生成的）： 
<UL>
<LI></A><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/porttype.java">PortType.java</A> 
<LI><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/porttypeimpl.java">PortTypeImpl.java</A> 
<LI><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/documenttype.java">DocumentType.java</A> 
<LI><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/documenttypeimpl.java">DocumentTypeImpl.java</A> 
<LI><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/webserviceconfiguration.java">WebServiceConfiguration.java</A> 
<LI><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/webserviceconfigurationimpl.java">WebServiceConfigurationImpl.java</A></LI></UL>
<P></P><A name=5>
<P><STRONG class=subhead>总结</STRONG> <BR>到目前为止，您应该对数据绑定的各个方面都比较熟悉了。我已初步介绍了您应该使用数据绑定的原因，尤其是在配置信息的范围内，并概述了为实现此方法您所需要了解的一些基本概念。</P>
<P>此系列文章的下一篇将继续考察数据绑定的过程。您将有机会去检查 <CODE>org.enhydra.xml.binding.SchemaMapper</CODE> 类，它将接受这第一部分中创建的 XML 方案作为数据接口，并从它创建出一个 Java 接口和实现类。本系列文章的第二部分将详细说明这一过程的每个步骤，并说明如何确保方案被准确表示，以便 XML 文档能接着被转换为生成的类的实例。</P>
<P>梳理一下您学到的 XML 方案和 JDOM （我将在示例代码中使用它们），下个月再见！</P></A><A name=resources>
<P><STRONG class=subhead>参考资料</STRONG> 
<UL>
<LI>下载包含本文中的示例代码的 zip 文件，包括所有 </A><A href="http://www-900.ibm.com/developerWorks/java/data-binding1/generated-files.zip">Java 类</A> 和 <A href="http://www-900.ibm.com/developerWorks/java/data-binding1/xmlfiles.zip">XML 文件</A>。 
<LI>从 David Megginson 的 SAX 页下载 <A href="http://www.megginson.com/SAX">The Simple API for XML (SAX)</A> 的 2000 年 5 月版，这是一个基于事件的 API，用于在 Java 应用程序中读取 XML。 
<LI>从 W3C DOM 工作小组的正式网页上不断了解 DOM 的最新状态，DOM 即<A href="http://www.w3.org/DOM">文档对象模型 (DOM)</A>，该模型是一种基于树的 API，用于在 Java 应用程序中读写 XML。 
<LI>下载 <A href="http://www.jdom.org/">JDOM</A>，这是一种使 XML 在 Java 应用程序中的使用更加简单和直观的 API，并了解有关 JDOM 方案的内容。 
<LI>研读 <A href="http://java.sun.com/aboutjava/communityprocess/jsr/jsr_031_xmld.html">JSR-031：数据绑定</A>，这是 Sun 的数据绑定规范要求。 
<LI>下载 <A href="http://www.enhydra.org/software/enhydra/index.html">Enhydra</A>，一种开放源代码的 Java 应用程序服务器。 
<LI>从 W3C 的网站阅读由两部分组成的 XML 方案规范（目前为工作初稿）的最新细节：<A href="http://www.w3.org/TR/xmlschema-1/">结构</A>和<A href="http://www.w3.org/TR/xmlschema-2/">数据类型</A>。 
<LI>Brett McLaughlin 有能力写一本关于 Java 应用程序和 XML 这一主题的书 -- 他也这样做了：订购《<A href="http://www.oreilly.com/catalog/javaxml"><I>Java and XML</I></A>》，由 O'Reilly 出版。 <!-- <li>Join the developerWorks <a href="news://news.software.ibm.com/ibm.software.developerworks.xml.javatools">XML 
tools and APIs newsgroup</a> for Java language developers to exchange info 
and opinions.</li> -->
<LI>需要关于 XML 更基本的导论吗？试一试 developerWorks 的 <A href="http://www-4.ibm.com/software/developerWorks/education/xmlintro/">XML 导论</A>教程和<A href="http://www2.software.ibm.com/developerWorks/education.nsf/xml-onlinecourse-bytitle">其它教学性内容</A>，其中包括了大多数基本主题。 </LI></UL>
<P></P><A name=author>
<P><STRONG class=subhead>作者简介</STRONG> <BR><!-- <img SRC="author.jpg" BORDER=0 width=64 height=71 align=LEFT> -->Brett McLaughlin 是 Lutris 科技公司的 Enhydra 战略家，其专长是分布式系统的体系结构。他是《</A><A href="http://www.oreilly.com/catalog/javaxml"><I>Java and XML</I></A>》(O'Reilly) 一书的作者。Brett 涉足多种技术，如 Java servlets、Enterprise JavaBeans 技术、XML 和企业对企业的应用程序等。最近他与 Jason Hunter 一起建立了 <A href="http://www.jdom.org/">JDOM</A> 方案，该方案提供简单的 API 来在 Java 应用程序中操作 XML。他还是 Apache Cocoon 项目和 EJBoss EJB 服务器的积极开发人员，并且是 Apache Turbine 项目的共同创始人之一。可以通过 <A href="mailto:brett@newInstance.com">brett@newInstance.com</A> 与 Brett 联系。</P><!-- End paper --><img src ="http://www.blogjava.net/jackybu/aggbug/2392.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-24 06:10 <a href="http://www.blogjava.net/jackybu/articles/2392.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAXP: Coding for Parser &amp; Transformer Independence</title><link>http://www.blogjava.net/jackybu/articles/2376.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 08:09:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2376.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2376.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2376.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2376.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2376.html</trackback:ping><description><![CDATA[Author:&nbsp;Henry&nbsp;Chen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;译者：李力<BR><B>介绍</B><BR>由于可扩展标记语言（XML）的易用性和轻携性，其近年来获得了极大的流行。它与Java结合起来，非常适用于可移植的数据和代码。每一个与XML文档打交道的Java程序员，无论是读数据，还是进行数据转换，都必须对Java&nbsp;API’s&nbsp;for&nbsp;XML&nbsp;Processing&nbsp;(JAXP)有一个很深的理解。编写XML解析器无关的代码有许多好处，JAXP&nbsp;API&nbsp;是用于XML的，就像JDBC&nbsp;API是用于关系型数据库的。这篇介绍性的文章帮助开发者学习JAXP&nbsp;API，并让开发者对可插入层（pluggability&nbsp;layer）有一个很深的理解，这样，开发者就可以在他们的应用程序中自如的更换解析器。<BR>JAXPack<BR>&nbsp;&nbsp;&nbsp;SUN推出了一个用于XML的Java&nbsp;API&nbsp;和架构，称其为Java&nbsp;XML&nbsp;Pack&nbsp;(JAXPack&nbsp;-&nbsp;<A class=l2 href="http://java.sun.com/xml/javaxmlpack.html" target=_blank>http://java.sun.com/xml/javaxmlpack.html</A>)。下载包中包括了现在行业中一些重要的标准。这篇文章中，我们将注意力放在JAXP（the&nbsp;API&nbsp;for&nbsp;XML&nbsp;Processing）上，Sybase的Easerver从版本3.6.1开始支持JAXP。<BR>&nbsp;&nbsp;&nbsp;首先，我们看一下JAXP提供的解析能力，解析XML文档有两种最基本的方法,&nbsp;基于事件的SAX和遍历树的DOM。开发者可以选择最适合他们需要的方法。让我们钻进去，深入的看一下这些API。<BR>这篇文章中，我们用图1中的XML文档来阐述我们的例子。<BR><IMG src="http://www.javaresearch.org/members/arli28/Figure1.jpg"><BR><BR>&nbsp;<BR><B>SAX</B><BR>Simple&nbsp;API&nbsp;for&nbsp;XML&nbsp;Parsing&nbsp;(SAX)是事件驱动的，它从头到尾遍历整个文档，&nbsp;当它遇到一个语法结构时，它会通知运行它的程序，这些是通过事件处理接口ContentHandler，&nbsp;ErrorHandler，&nbsp;DTDHandler，&nbsp;和&nbsp;EntityResolver中的回调方法实现的。这些回调方法可以被开发者自定义实现来满足一些特殊的要求。图2描绘了SAX解析器解析文档是各种组件之间的关系。<BR>&nbsp;<IMG src="http://www.javaresearch.org/members/arli28/Figure2.jpg"><BR><BR>我们将遍历图1中XML文档，并且给出SAX解析器一行一行的解析是调用回调方法的细节，在这个例子中，我们不包括对ignorableWhiteSpace方法调用。<BR><IMG src="http://www.javaresearch.org/members/arli28/Figure3.jpg"><BR><BR>现在你已经对SAX如何工作有一个总体的了解，接下来，让我们看一看用真真的Java代码实现的例子，我们实现的程序的完整代码可以在http://www.sybase.com/developer.上找到，出于这篇文章的目的，我将只用一些代码相关部分的片断。<BR><BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>class</FONT></B>&nbsp;SAXExample&nbsp;<B><FONT color=#0000ff>extends</FONT></B>&nbsp;<FONT color=#ff0000>DefaultHandler</FONT>&nbsp;{ 
<LI><FONT color=#ff0000>SAXParserFactory</FONT>&nbsp;factory&nbsp;=&nbsp;<FONT color=#ff0000>SAXParserFactory</FONT>.newInstance(); 
<LI><FONT color=#ff0000>SAXParser</FONT>&nbsp;saxParser&nbsp;=&nbsp;factory.newSAXParser(); 
<LI>
<LI><FONT color=#ff0000>DefaultHandler</FONT>&nbsp;handler&nbsp;=&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;SAXExample(); 
<LI>saxParser.parse(&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;<FONT color=#ff0000>File</FONT>(argv[0]),&nbsp;handler) </LI></OL></DIV><BR>&nbsp;<BR>&nbsp;&nbsp;注意我们继承了DefaultHandler&nbsp;Class，&nbsp;这个类用一些空方法实现了ContentHandler,&nbsp;ErrorHandle,&nbsp;DTDHandler,和&nbsp;EntityResolver接口，这样，程序员就可以只覆盖一些他们需要的方法。<BR>在我们解析之前，我们首先需要通过调用newInstance方法，实例化一个SAXParserFactory,这个方法用某个特定的查找顺序来决定使用哪一个SAXParserFactory的实现，这就意味着，解析器更改时，代码无需重新编译。<BR>一旦我们实例化了一个SAXParserFactory，我们可以设置三个选项，这些决定了随后如何产生SAXParser的对象。<BR>SAXParserFactory&nbsp;&nbsp;使namespace可用<BR>SetValidating&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;打开验证<BR>SetFeature&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;设定底层实现的特征<BR>SAXParserFactory配置好后，我们调用newSAXParser方法来实例化一个JAXP&nbsp;SAXParser对象，这个对象包装了一个底层的SAX解析器，并且允许我们以厂商中立的方式与其交互，现在，我们就可以解析了。在这个例子中，我们用File对象作为输入，它还可以接受其他的输入源，如InputSource对象，InputStream&nbsp;对象，或者Uniform&nbsp;Resource&nbsp;Identifier&nbsp;(URI)。<BR>注意程序是如何使自己成为解析器的处理者(handler)的，这意味着解析器将调用SAXExampl中的回调方法的，当解析方法一行一行的解析XML文件时，我们的处理类中的回调事件就发生了。<BR><BR><B>DOM</B><BR>Document&nbsp;Object&nbsp;Model&nbsp;(DOM)是将XML文档解析成树状对象的一套接口，每一个对象，或结点(node)都有一个用org.w3c.dom包中的接口表示的类型(type).如Element,&nbsp;Attribute,<BR>Comment,&nbsp;和Text。可以像操作其他任何树状数据结构一样来操作DOM树状对象，它允许随机访问XML文档中特定部分的数据，并且修改它，这些是SAX解析器做不到的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这种方法的缺点是它非常占用内存和CPU资源，因为构建DOM树时需要将整个XML文档读入并保持在内存中。<BR>&nbsp;<IMG src="http://www.javaresearch.org/members/arli28/Figure4.jpg"><BR><BR>让我们看一个例子：<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>class</FONT></B>&nbsp;DOMExample&nbsp;{ 
<LI><FONT color=#ff0000>DocumentBuilderFactory</FONT>&nbsp;factory&nbsp;=&nbsp;<FONT color=#ff0000>DocumentBuilderFactory</FONT>.newInstance(); 
<LI><FONT color=#ff0000>DocumentBuilder</FONT>&nbsp;domParser&nbsp;=&nbsp;factory.newDocumentBuilder(); 
<LI><FONT color=#ff0000>Document</FONT>&nbsp;document&nbsp;=&nbsp;domParser.parse(&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;<FONT color=#ff0000>File</FONT>(argv[0])&nbsp;); </LI></OL></DIV><BR>.<BR>与SAX类似，我们首先用newInstance方法实例化一个DocumentBuilderFactory对象，同样类似SAXParserFactory，工厂可以配置用来处理namespace(命名空间)和validation（验证）。另外，还有一些其他可选的设置，但这已经超出了本文的范围。工厂对象准备好后，我们就可以创造一个DocumentBuilder对象，它可用来解析xml文件和创造Document对象，同样类似SAXParser的parse方法，Document对象可以接受InputSource对象，InputStream对象，或者URI。<BR><BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><FONT color=#ff0000>Node</FONT>&nbsp;thePhonebook&nbsp;=&nbsp;document.getDocumentElement(); 
<LI><FONT color=#ff0000>NodeList</FONT>&nbsp;personList&nbsp;=&nbsp;thePhonebook.getChildNodes(); 
<LI>
<LI><FONT color=#ff0000>Node</FONT>&nbsp;currPerson&nbsp;=&nbsp;personList.item(0); 
<LI><FONT color=#ff0000>Node</FONT>&nbsp;fullName&nbsp;=&nbsp;currPerson.getChildNodes().item(0); 
<LI><FONT color=#ff0000>Node</FONT>&nbsp;firstName&nbsp;=&nbsp;fullName.getChildNodes().item(0); 
<LI><FONT color=#ff0000>Node</FONT>&nbsp;lastName&nbsp;=&nbsp;fullName.getChildNodes().item(1); 
<LI><FONT color=#ff0000>Text</FONT>&nbsp;firstNameText&nbsp;=&nbsp;(<FONT color=#ff0000>Text</FONT>)firstName.getFirstChild(); 
<LI><FONT color=#ff0000>Text</FONT>&nbsp;lastNameText&nbsp;=&nbsp;(<FONT color=#ff0000>Text</FONT>)lastName.getFirstChild(); 
<LI>
<LI><FONT color=#ff0000>Node</FONT>&nbsp;phone&nbsp;=&nbsp;currPerson.getChildNodes().item(1); 
<LI><FONT color=#ff0000>Node</FONT>&nbsp;workPhone&nbsp;=&nbsp;phone.getChildNodes().item(0); 
<LI><FONT color=#ff0000>Node</FONT>&nbsp;homePhone&nbsp;=&nbsp;phone.getChildNodes().item(1); 
<LI><FONT color=#ff0000>Text</FONT>&nbsp;workPhoneText&nbsp;=&nbsp;(<FONT color=#ff0000>Text</FONT>)workPhone.getFirstChild(); 
<LI><FONT color=#ff0000>Text</FONT>&nbsp;homePhoneText&nbsp;=&nbsp;(<FONT color=#ff0000>Text</FONT>)homePhone.getFirstChild(); </LI></OL></DIV><BR>一旦我们拥有了Document&nbsp;DOM对象，我们就可以像操作其他树一样操作它。getDocumentElement方法返回根元素，从根元素可以得到子节点的NodeList，并且可以处理它们。在DOM树结构的叶结点，我们可以找到Text对象，它继承了Node。调用getData方法可以返回字符串的值。如你所见，使用者在操作数据时必须对文档数据的结构有一个了解，而在SAX中，解析器仅仅对它遇到的数据反应。<BR>但是，DOM最大的优点是它可以对数据结构进行修改，例如：<BR><BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><B><FONT color=#0000ff>if</FONT></B>&nbsp;(firstNameText.getData().equals(<FONT color=#ff33ff>"Richard"</FONT>)&nbsp;&amp;&amp; 
<LI>lastNameText.getData().equals(<FONT color=#ff33ff>"Mullins"</FONT>))&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;homePhoneText.setNodeValue(<FONT color=#ff33ff>"(510)333-3333"</FONT>); 
<LI>} 
<LI></LI></OL></DIV><BR>用setNodeValue方法可以改变DOM树中的数据，随后，我们将看XSLT如何将一个新树写入一个数据文件。<BR><B>XSLT</B><BR>XSL转换(XSLT)是将XML文档转换为其他XML文档或其他格式的文档（如HTML）的一组API,&nbsp;&nbsp;XML样式语言（XSL）在转换中作用巨大，用其定义的样式表包含了格式规则，指定了文档如何显示。<IMG src="http://www.javaresearch.org/members/arli28/Figure5.jpg"><BR><BR>&nbsp;<BR>这里有一个例子，将DOM对象转换为XML文档：<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><I><FONT color=#339900>//create&nbsp;a&nbsp;new&nbsp;DOMSource&nbsp;using&nbsp;the&nbsp;root&nbsp;node&nbsp;of&nbsp;an&nbsp;existing&nbsp;DOM&nbsp;tree</FONT></I> 
<LI><FONT color=#ff0000>DOMSource</FONT>&nbsp;source&nbsp;=&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;<FONT color=#ff0000>DOMSource</FONT>(thePhonebook); 
<LI><FONT color=#ff0000>StreamResult</FONT>&nbsp;result&nbsp;=&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;<FONT color=#ff0000>StreamResult</FONT>(<B><A href="http://www.javaresearch.org/source/jdk142/java/lang/System.java.html" target=_blank><FONT class=classLink><U>System</U></FONT></A></B>.out); 
<LI>
<LI><FONT color=#ff0000>TransformerFactory</FONT>&nbsp;tFactory&nbsp;=&nbsp;<FONT color=#ff0000>TransformerFactory</FONT>.newInstance(); 
<LI><FONT color=#ff0000>Transformer</FONT>&nbsp;transformer&nbsp;=&nbsp;tFactory.newTransformer(); 
<LI>transformer.transform(source,&nbsp;result); </LI></OL></DIV><BR>我们首先用newInstance方法实例化一个TransformerFactory对象，它用特定的查找顺序来决定使用哪一个转换器实现。和SAX和DOM工厂一样，可以对TransformerFactory对象进行一些配置，来影响Transformer对象的创建。用newTransformer方法创建好Transformer对象后，就可以调用transform方法，它将一个Source对象（DOMSource，SAXSource,&nbsp;StreamSource）转换为Result对象（DOMResult,&nbsp;SAXResult,&nbsp;StreamResult）。<BR>抽象层<BR>前面已经提到过，用某种特定的查找顺序来决定使用哪个SAX,&nbsp;DOM&nbsp;和XSLT的实现，下面的API定义了查找顺序：<BR>&nbsp;.使用javax.xml.parsers.SAXParserFactory（或javax.xml.parsers.DocumentBuilderFactory）系统属性（system&nbsp;property）。<BR>&nbsp;.使用JRE目录中“lib/jaxp.properties”属性文件定义的javax.xml.parsers.SAXParserFactory属性的值，这个值必须包括了实现类的全名。<BR>&nbsp;.使用Service&nbsp;API,它可以在运行系统中使用的JAR文件中包括的META-INF/services/javax.xml.parsers.SAXParserFactory文件中查找类名。<BR>&nbsp;.使用平台默认的SAXParserFactory实例。<BR>使用DOM时，你只需将javax.xml.parsers.SAXParserFactory替换为javax.xml.parsers.DocumentBuilderFactory，类似，使用XSLT时，你用javax.xml.transform.TransformerFactory替换。<BR><B>总结</B><BR>如你所见，你写的代码只需与抽象层打交道。这保证了厂商的无关性，你可以快速和轻松的替换你的解析器的实现。解析XML文档时，Java开发者可以根据他们的需要设置两个选项。SAX是利用回调过程的事件驱动模型，而DOM是一种游历树的模型，它在操作数据前必须将XML文档解析成树，XSLT则可以将XML文档转换为另一种XML文档或者其他格式，如HTML。总之，JAXP非常的强大，灵活，它提供的简单可用的工具可以满足大多数Java开发者处理XML文档时的需要。<BR><img src ="http://www.blogjava.net/jackybu/aggbug/2376.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-23 16:09 <a href="http://www.blogjava.net/jackybu/articles/2376.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用于XML的简单API</title><link>http://www.blogjava.net/jackybu/articles/2371.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 07:19:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2371.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2371.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2371.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2371.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2371.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Eric Armstrong 本章主要讨论Simple API for XML (SAX)，它是一种事件驱动、串行存取XML文档的机制。这是多数servlet和面向网络的程序用来传送和接收XML文档的协议，因为在目前XML文档处理的可用机制中它最快并且所需内存最少。 SAX协议比文档对象模型(DOM)需要进行更多的编程。它是事件驱动模型(你提供回调方法，解析器在读取XML数据的时候调用它们...&nbsp;&nbsp;<a href='http://www.blogjava.net/jackybu/articles/2371.html'>阅读全文</a><img src ="http://www.blogjava.net/jackybu/aggbug/2371.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-23 15:19 <a href="http://www.blogjava.net/jackybu/articles/2371.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>文档对象模型</title><link>http://www.blogjava.net/jackybu/articles/2370.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 07:18:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2370.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2370.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2370.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2370.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2370.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Eric Armstrong SAX一章中已经编写了包含用于放映的幻灯片的XML文件。然后又使用SAX API将XML回送到显示器。 本章中，将使用文档对象模型(DOM)建立一个小的SlideShow应用程序。首先构建DOM并查看它，然后看看如何编写XML结构的DOM ，将它显示在GUI中，并且操作树结构。 文档对象模型是普通的树结构，每个节点包含一个来自于XML结构的组件。两种最常见...&nbsp;&nbsp;<a href='http://www.blogjava.net/jackybu/articles/2370.html'>阅读全文</a><img src ="http://www.blogjava.net/jackybu/aggbug/2370.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-23 15:18 <a href="http://www.blogjava.net/jackybu/articles/2370.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用dom4j建立,修改XML文档,并解决格式化输出和中文问题 </title><link>http://www.blogjava.net/jackybu/articles/2360.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 05:14:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2360.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2360.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2360.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2360.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2360.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&lt;books&gt;&nbsp; &lt;!--This is a test for dom4j, holen, 2004.9.11--&gt;&nbsp; &lt;book show="no"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;Dom4j Tutorials&lt;/title&g...&nbsp;&nbsp;<a href='http://www.blogjava.net/jackybu/articles/2360.html'>阅读全文</a><img src ="http://www.blogjava.net/jackybu/aggbug/2360.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-23 13:14 <a href="http://www.blogjava.net/jackybu/articles/2360.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DOM4J 使用简介 </title><link>http://www.blogjava.net/jackybu/articles/2359.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 05:12:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2359.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2359.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2359.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2359.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2359.html</trackback:ping><description><![CDATA[<FONT face=Arial>Dom4j 使用简介<BR><BR>作者：冰云 icecloud(AT)sina.com<BR><BR>时间：2003.12.15<BR><BR><BR><BR>版权声明：<BR><BR>本文由冰云完成，首发于CSDN，未经许可，不得使用于任何商业用途。<BR><BR>文中代码部分引用自DOM4J文档。<BR><BR>欢迎转载，但请保持文章及版权声明完整。<BR><BR>如需联络请发邮件：icecloud(AT)sina.com<BR><BR><BR><BR><BR>DOM4J是dom4j.org出品的一个开源XML解析包，它的网站中这样定义：<BR><BR>Dom4j is an easy to use, open source library for working with XML, XPath and XSLT on the Java platform using the Java Collections Framework and with full support for DOM, SAX and JAXP.<BR><BR>Dom4j是一个易用的、开源的库，用于XML，XPath和XSLT。它应用于Java平台，采用了Java集合框架并完全支持DOM，SAX和JAXP。<BR><BR>DOM4J使用起来非常简单。只要你了解基本的XML-DOM模型，就能使用。然而他自己带的指南只有短短一页（html），不过说的到挺全。国内的中文资料很少。因而俺写这个短小的教程方便大家使用，这篇文章仅谈及基本的用法，如需深入的使用，请……自己摸索或查找别的资料。<BR><BR>之前看过IBM developer社区的文章（参见附录），提到一些XML解析包的性能比较，其中DOM4J的性能非常出色，在多项测试中名列前茅。（事实上DOM4J的官方文档中也引用了这个比较）所以这次的项目中我采用了DOM4J作为XML解析工具。<BR><BR>在国内比较流行的是使用JDOM作为解析器，两者各擅其长，但DOM4J最大的特色是使用大量的接口，这也是它被认为比JDOM灵活的主要原因。大师不是说过么，“面向接口编程”。目前使用DOM4J的已经越来越多。如果你善于使用JDOM，不妨继续用下去，只看看本篇文章作为了解与比较，如果你正要采用一种解析器，不如就用DOM4J吧。 <BR><BR>它的主要接口都在org.dom4j这个包里定义：<BR><BR>Attribute<BR>Attribute定义了XML的属性<BR><BR>Branch<BR>Branch为能够包含子节点的节点如XML元素(Element)和文档(Docuemnts)定义了一个公共的行为，<BR><BR>CDATA<BR>CDATA 定义了XML CDATA 区域<BR><BR>CharacterData<BR>CharacterData是一个标识借口，标识基于字符的节点。如CDATA，Comment, Text.<BR><BR>Comment<BR>Comment 定义了XML注释的行为<BR><BR>Document<BR>定义了XML文档<BR><BR>DocumentType<BR>DocumentType 定义XML DOCTYPE声明<BR><BR>Element<BR>Element定义XML 元素<BR><BR>ElementHandler<BR>ElementHandler定义了 Element 对象的处理器<BR><BR>ElementPath<BR>被 ElementHandler 使用，用于取得当前正在处理的路径层次信息<BR><BR>Entity<BR>Entity定义 XML entity<BR><BR>Node<BR>Node为所有的dom4j中XML节点定义了多态行为<BR><BR>NodeFilter<BR>NodeFilter 定义了在dom4j节点中产生的一个滤镜或谓词的行为（predicate）<BR><BR>ProcessingInstruction<BR>ProcessingInstruction 定义 XML 处理指令.<BR><BR>Text<BR>Text 定义XML 文本节点.<BR><BR>Visitor<BR>Visitor 用于实现Visitor模式.<BR><BR>XPath<BR>XPath 在分析一个字符串后会提供一个XPath 表达式<BR><BR><BR>看名字大致就知道它们的涵义如何了。<BR><BR>要想弄懂这套接口，关键的是要明白接口的继承关系：<BR><BR>interface java.lang.Cloneable <BR>interface org.dom4j.Node <BR>interface org.dom4j.Attribute <BR>interface org.dom4j.Branch <BR>interface org.dom4j.Document <BR>interface org.dom4j.Element <BR>interface org.dom4j.CharacterData <BR>interface org.dom4j.CDATA <BR>interface org.dom4j.Comment <BR>interface org.dom4j.Text <BR>interface org.dom4j.DocumentType <BR>interface org.dom4j.Entity <BR>interface org.dom4j.ProcessingInstruction <BR>一目了然，很多事情都清楚了。大部分都是由Node继承来的。知道这些关系，将来写程序就不会出现ClassCastException了。<BR><BR>下面给出一些例子（部分摘自DOM4J自带的文档），简单说一下如何使用。<BR><BR>１． 读取并解析XML文档：<BR><BR>读写XML文档主要依赖于org.dom4j.io包，其中提供DOMReader和SAXReader两类不同方式，而调用方式是一样的。这就是依靠接口的好处。<BR><BR><BR>// 从文件读取XML，输入文件名，返回XML文档<BR><BR>public Document read(String fileName) throws MalformedURLException, DocumentException {<BR><BR>SAXReader reader = new SAXReader();<BR><BR>Document document = reader.read(new File(fileName));<BR><BR>return document;<BR><BR>}<BR><BR><BR><BR><BR>其中，reader的read方法是重载的，可以从InputStream, File, Url等多种不同的源来读取。得到的Document对象就带表了整个XML。<BR><BR>根据本人自己的经验，读取的字符编码是按照XML文件头定义的编码来转换。如果遇到乱码问题，注意要把各处的编码名称保持一致即可。<BR><BR>２． 取得Root节点<BR><BR>读取后的第二步，就是得到Root节点。熟悉XML的人都知道，一切XML分析都是从Root元素开始的。<BR><BR><BR>　 public Element getRootElement(Document doc){<BR><BR>return doc.getRootElement();<BR><BR>}<BR><BR><BR><BR><BR>３． 遍历XML树<BR><BR>DOM4J提供至少3种遍历节点的方法：<BR><BR>1) 枚举(Iterator)<BR><BR><BR>// 枚举所有子节点<BR><BR>for ( Iterator i = root.elementIterator(); i.hasNext(); ) {<BR><BR>Element element = (Element) i.next();<BR><BR>// do something<BR><BR>}<BR><BR>// 枚举名称为foo的节点<BR><BR>for ( Iterator i = root.elementIterator(foo); i.hasNext();) {<BR><BR>Element foo = (Element) i.next();<BR><BR>// do something<BR><BR>}<BR><BR>// 枚举属性<BR><BR>for ( Iterator i = root.attributeIterator(); i.hasNext(); ) {<BR><BR>Attribute attribute = (Attribute) i.next();<BR><BR>// do something<BR><BR>}<BR><BR><BR><BR>2)递归 <BR><BR>递归也可以采用Iterator作为枚举手段，但文档中提供了另外的做法<BR><BR><BR>public void treeWalk() {<BR><BR>treeWalk(getRootElement());<BR><BR>}<BR><BR>public void treeWalk(Element element) {<BR><BR>for (int i = 0, size = element.nodeCount(); i &lt; size; i++) {<BR><BR>Node node = element.node(i);<BR><BR>if (node instanceof Element) {<BR><BR>treeWalk((Element) node);<BR><BR>} else { // do something....<BR><BR>}<BR><BR>}<BR><BR>}<BR><BR><BR><BR><BR>3) Visitor模式<BR><BR>最令人兴奋的是DOM4J对Visitor的支持，这样可以大大缩减代码量，并且清楚易懂。了解设计模式的人都知道，Visitor是GOF设计模式之一。其主要原理就是两种类互相保有对方的引用，并且一种作为Visitor去访问许多Visitable。我们来看DOM4J中的Visitor模式(快速文档中没有提供)<BR><BR>只需要自定一个类实现Visitor接口即可。<BR><BR><BR>　 public class MyVisitor extends VisitorSupport {<BR><BR>public void visit(Element element){<BR><BR>System.out.println(element.getName());<BR><BR>}<BR><BR>public void visit(Attribute attr){<BR><BR>System.out.println(attr.getName());<BR><BR>}<BR><BR>}<BR><BR>调用： root.accept(new MyVisitor())<BR><BR><BR><BR>Visitor接口提供多种Visit()的重载，根据XML不同的对象，将采用不同的方式来访问。上面是给出的Element和Attribute的简单实现，一般比较常用的就是这两个。VisitorSupport是DOM4J提供的默认适配器，Visitor接口的Default Adapter模式，这个模式给出了各种visit(*)的空实现，以便简化代码。<BR><BR>注意，这个Visitor是自动遍历所有子节点的。如果是root.accept(MyVisitor)，将遍历子节点。我第一次用的时候，认为是需要自己遍历，便在递归中调用Visitor，结果可想而知。<BR><BR>4. XPath支持<BR><BR>DOM4J对XPath有良好的支持，如访问一个节点，可直接用XPath选择。<BR><BR><BR><BR>public void bar(Document document) {<BR><BR>List list = document.selectNodes( //foo/bar );<BR><BR>Node node = document.selectSingleNode(//foo/bar/author);<BR><BR>String name = node.valueOf( @name );<BR><BR>}<BR><BR><BR><BR><BR>例如，如果你想查找XHTML文档中所有的超链接，下面的代码可以实现： <BR><BR><BR><BR>public void findLinks(Document document) throws DocumentException {<BR><BR>List list = document.selectNodes( //a/@href );<BR><BR>for (Iterator iter = list.iterator(); iter.hasNext(); ) {<BR><BR>Attribute attribute = (Attribute) iter.next();<BR><BR>String url = attribute.getValue();<BR><BR>}<BR><BR>}<BR><BR><BR><BR><BR>5. 字符串与XML的转换<BR><BR>有时候经常要用到字符串转换为XML或反之，<BR><BR><BR>// XML转字符串 <BR>　 Document document = ...;<BR><BR>String text = document.asXML();<BR><BR>// 字符串转XML<BR><BR>String text = &lt;person&gt; &lt;name&gt;James&lt;/name&gt; &lt;/person&gt;;<BR><BR>Document document = DocumentHelper.parseText(text);<BR><BR><BR><BR><BR>6 用XSLT转换XML<BR><BR><BR><BR>public Document styleDocument(<BR><BR>Document document, <BR><BR>String stylesheet<BR><BR>) throws Exception {<BR><BR>// load the transformer using JAXP<BR><BR>TransformerFactory factory = TransformerFactory.newInstance();<BR><BR>Transformer transformer = factory.newTransformer( <BR><BR>new StreamSource( stylesheet ) <BR><BR>);<BR><BR>// now lets style the given document<BR><BR>DocumentSource source = new DocumentSource( document );<BR><BR>DocumentResult result = new DocumentResult();<BR><BR>transformer.transform( source, result );<BR><BR>// return the transformed document<BR><BR>Document transformedDoc = result.getDocument();<BR><BR>return transformedDoc;<BR><BR>}<BR><BR><BR><BR><BR>7. 创建XML<BR><BR>一般创建XML是写文件前的工作，这就像StringBuffer一样容易。<BR><BR><BR><BR>public Document createDocument() {<BR><BR>Document document = DocumentHelper.createDocument();<BR><BR>Element root = document.addElement(root);<BR><BR>Element author1 =<BR><BR>root<BR><BR>.addElement(author)<BR><BR>.addAttribute(name, James)<BR><BR>.addAttribute(location, UK)<BR><BR>.addText(James Strachan);<BR><BR>Element author2 =<BR><BR>root<BR><BR>.addElement(author)<BR><BR>.addAttribute(name, Bob)<BR><BR>.addAttribute(location, US)<BR><BR>.addText(Bob McWhirter);<BR><BR>return document;<BR><BR>}<BR><BR><BR><BR><BR>8. 文件输出<BR><BR>一个简单的输出方法是将一个Document或任何的Node通过write方法输出<BR><BR><BR>FileWriter out = new FileWriter( foo.xml );<BR><BR>document.write(out);<BR><BR><BR><BR><BR>如果你想改变输出的格式，比如美化输出或缩减格式，可以用XMLWriter类<BR><BR>public void write(Document document) throws IOException {<BR><BR>// 指定文件<BR><BR>XMLWriter writer = new XMLWriter(<BR><BR>new FileWriter( output.xml )<BR><BR>);<BR><BR>writer.write( document );<BR><BR>writer.close();<BR><BR>// 美化格式<BR><BR>OutputFormat format = OutputFormat.createPrettyPrint();<BR><BR>writer = new XMLWriter( System.out, format );<BR><BR>writer.write( document );<BR><BR>// 缩减格式<BR><BR>format = OutputFormat.createCompactFormat();<BR><BR>writer = new XMLWriter( System.out, format );<BR><BR>writer.write( document );<BR><BR>}<BR><BR><BR><BR><BR>如何，DOM4J够简单吧，当然，还有一些复杂的应用没有提到，如ElementHandler等。如果你动心了，那就一起来用DOM4J.<BR><BR>DOM4J官方网站：(我老连不上)<BR><BR></FONT><A href="http://www.dom4j.org/" target=_blank><STRONG><FONT face=Arial>http://www.dom4j.org</FONT></STRONG></A><BR><BR><FONT face=Arial>DOM4J下载(SourceForge)，最新版本为1.4<BR><BR></FONT><A href="http://sourceforge.net/projects/dom4j" target=_blank><STRONG><FONT face=Arial>http://sourceforge.net/projects/dom4j</FONT></STRONG></A><BR><BR><FONT face=Arial>参考资料：<BR><BR>DOM4J文档<BR><BR>Java 中的 XML：文档模型，第一部分：性能<BR><BR></FONT><A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml" target=_blank><STRONG><FONT face=Arial>http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml</FONT></STRONG></A><BR><BR><FONT face=Arial>Java 中的 XML：Java 文档模型的用法<BR><BR></FONT><A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava2/index.shtml" target=_blank><STRONG><FONT face=Arial>http://www-900.ibm.com/developerWorks/cn/xml/x-injava2/index.shtml</FONT></STRONG></A><BR><BR><FONT face=Arial>Java XML API 漫谈　by robbin<BR><BR></FONT><A href="http://www.hibernate.org.cn:8000/137.html" target=_blank><STRONG><FONT face=Arial>http://www.hibernate.org.cn:8000/137.html</FONT></STRONG></A><BR>
<P><FONT face=Arial>Dom4j 使用简介<BR><BR>作者：冰云 icecloud(AT)sina.com<BR><BR>时间：2003.12.15<BR><BR><BR><BR>版权声明：<BR><BR>本文由冰云完成，首发于CSDN，未经许可，不得使用于任何商业用途。<BR><BR>文中代码部分引用自DOM4J文档。<BR><BR>欢迎转载，但请保持文章及版权声明完整。<BR><BR>如需联络请发邮件：icecloud(AT)sina.com<BR><BR><BR><BR><BR>DOM4J是dom4j.org出品的一个开源XML解析包，它的网站中这样定义：<BR><BR>Dom4j is an easy to use, open source library for working with XML, XPath and XSLT on the Java platform using the Java Collections Framework and with full support for DOM, SAX and JAXP.<BR><BR>Dom4j是一个易用的、开源的库，用于XML，XPath和XSLT。它应用于Java平台，采用了Java集合框架并完全支持DOM，SAX和JAXP。<BR><BR>DOM4J使用起来非常简单。只要你了解基本的XML-DOM模型，就能使用。然而他自己带的指南只有短短一页（html），不过说的到挺全。国内的中文资料很少。因而俺写这个短小的教程方便大家使用，这篇文章仅谈及基本的用法，如需深入的使用，请……自己摸索或查找别的资料。<BR><BR>之前看过IBM developer社区的文章（参见附录），提到一些XML解析包的性能比较，其中DOM4J的性能非常出色，在多项测试中名列前茅。（事实上DOM4J的官方文档中也引用了这个比较）所以这次的项目中我采用了DOM4J作为XML解析工具。<BR><BR>在国内比较流行的是使用JDOM作为解析器，两者各擅其长，但DOM4J最大的特色是使用大量的接口，这也是它被认为比JDOM灵活的主要原因。大师不是说过么，“面向接口编程”。目前使用DOM4J的已经越来越多。如果你善于使用JDOM，不妨继续用下去，只看看本篇文章作为了解与比较，如果你正要采用一种解析器，不如就用DOM4J吧。 <BR><BR>它的主要接口都在org.dom4j这个包里定义：<BR><BR>Attribute<BR>Attribute定义了XML的属性<BR><BR>Branch<BR>Branch为能够包含子节点的节点如XML元素(Element)和文档(Docuemnts)定义了一个公共的行为，<BR><BR>CDATA<BR>CDATA 定义了XML CDATA 区域<BR><BR>CharacterData<BR>CharacterData是一个标识借口，标识基于字符的节点。如CDATA，Comment, Text.<BR><BR>Comment<BR>Comment 定义了XML注释的行为<BR><BR>Document<BR>定义了XML文档<BR><BR>DocumentType<BR>DocumentType 定义XML DOCTYPE声明<BR><BR>Element<BR>Element定义XML 元素<BR><BR>ElementHandler<BR>ElementHandler定义了 Element 对象的处理器<BR><BR>ElementPath<BR>被 ElementHandler 使用，用于取得当前正在处理的路径层次信息<BR><BR>Entity<BR>Entity定义 XML entity<BR><BR>Node<BR>Node为所有的dom4j中XML节点定义了多态行为<BR><BR>NodeFilter<BR>NodeFilter 定义了在dom4j节点中产生的一个滤镜或谓词的行为（predicate）<BR><BR>ProcessingInstruction<BR>ProcessingInstruction 定义 XML 处理指令.<BR><BR>Text<BR>Text 定义XML 文本节点.<BR><BR>Visitor<BR>Visitor 用于实现Visitor模式.<BR><BR>XPath<BR>XPath 在分析一个字符串后会提供一个XPath 表达式<BR><BR><BR>看名字大致就知道它们的涵义如何了。<BR><BR>要想弄懂这套接口，关键的是要明白接口的继承关系：<BR><BR>interface java.lang.Cloneable <BR>interface org.dom4j.Node <BR>interface org.dom4j.Attribute <BR>interface org.dom4j.Branch <BR>interface org.dom4j.Document <BR>interface org.dom4j.Element <BR>interface org.dom4j.CharacterData <BR>interface org.dom4j.CDATA <BR>interface org.dom4j.Comment <BR>interface org.dom4j.Text <BR>interface org.dom4j.DocumentType <BR>interface org.dom4j.Entity <BR>interface org.dom4j.ProcessingInstruction <BR>一目了然，很多事情都清楚了。大部分都是由Node继承来的。知道这些关系，将来写程序就不会出现ClassCastException了。<BR><BR>下面给出一些例子（部分摘自DOM4J自带的文档），简单说一下如何使用。<BR><BR>１． 读取并解析XML文档：<BR><BR>读写XML文档主要依赖于org.dom4j.io包，其中提供DOMReader和SAXReader两类不同方式，而调用方式是一样的。这就是依靠接口的好处。<BR><BR><BR>// 从文件读取XML，输入文件名，返回XML文档<BR><BR>public Document read(String fileName) throws MalformedURLException, DocumentException {<BR><BR>SAXReader reader = new SAXReader();<BR><BR>Document document = reader.read(new File(fileName));<BR><BR>return document;<BR><BR>}<BR><BR><BR><BR><BR>其中，reader的read方法是重载的，可以从InputStream, File, Url等多种不同的源来读取。得到的Document对象就带表了整个XML。<BR><BR>根据本人自己的经验，读取的字符编码是按照XML文件头定义的编码来转换。如果遇到乱码问题，注意要把各处的编码名称保持一致即可。<BR><BR>２． 取得Root节点<BR><BR>读取后的第二步，就是得到Root节点。熟悉XML的人都知道，一切XML分析都是从Root元素开始的。<BR><BR><BR>　 public Element getRootElement(Document doc){<BR><BR>return doc.getRootElement();<BR><BR>}<BR><BR><BR><BR><BR>３． 遍历XML树<BR><BR>DOM4J提供至少3种遍历节点的方法：<BR><BR>1) 枚举(Iterator)<BR><BR><BR>// 枚举所有子节点<BR><BR>for ( Iterator i = root.elementIterator(); i.hasNext(); ) {<BR><BR>Element element = (Element) i.next();<BR><BR>// do something<BR><BR>}<BR><BR>// 枚举名称为foo的节点<BR><BR>for ( Iterator i = root.elementIterator(foo); i.hasNext();) {<BR><BR>Element foo = (Element) i.next();<BR><BR>// do something<BR><BR>}<BR><BR>// 枚举属性<BR><BR>for ( Iterator i = root.attributeIterator(); i.hasNext(); ) {<BR><BR>Attribute attribute = (Attribute) i.next();<BR><BR>// do something<BR><BR>}<BR><BR><BR><BR>2)递归 <BR><BR>递归也可以采用Iterator作为枚举手段，但文档中提供了另外的做法<BR><BR><BR>public void treeWalk() {<BR><BR>treeWalk(getRootElement());<BR><BR>}<BR><BR>public void treeWalk(Element element) {<BR><BR>for (int i = 0, size = element.nodeCount(); i &lt; size; i++) {<BR><BR>Node node = element.node(i);<BR><BR>if (node instanceof Element) {<BR><BR>treeWalk((Element) node);<BR><BR>} else { // do something....<BR><BR>}<BR><BR>}<BR><BR>}<BR><BR><BR><BR><BR>3) Visitor模式<BR><BR>最令人兴奋的是DOM4J对Visitor的支持，这样可以大大缩减代码量，并且清楚易懂。了解设计模式的人都知道，Visitor是GOF设计模式之一。其主要原理就是两种类互相保有对方的引用，并且一种作为Visitor去访问许多Visitable。我们来看DOM4J中的Visitor模式(快速文档中没有提供)<BR><BR>只需要自定一个类实现Visitor接口即可。<BR><BR><BR>　 public class MyVisitor extends VisitorSupport {<BR><BR>public void visit(Element element){<BR><BR>System.out.println(element.getName());<BR><BR>}<BR><BR>public void visit(Attribute attr){<BR><BR>System.out.println(attr.getName());<BR><BR>}<BR><BR>}<BR><BR>调用： root.accept(new MyVisitor())<BR><BR><BR><BR>Visitor接口提供多种Visit()的重载，根据XML不同的对象，将采用不同的方式来访问。上面是给出的Element和Attribute的简单实现，一般比较常用的就是这两个。VisitorSupport是DOM4J提供的默认适配器，Visitor接口的Default Adapter模式，这个模式给出了各种visit(*)的空实现，以便简化代码。<BR><BR>注意，这个Visitor是自动遍历所有子节点的。如果是root.accept(MyVisitor)，将遍历子节点。我第一次用的时候，认为是需要自己遍历，便在递归中调用Visitor，结果可想而知。<BR><BR>4. XPath支持<BR><BR>DOM4J对XPath有良好的支持，如访问一个节点，可直接用XPath选择。<BR><BR><BR><BR>public void bar(Document document) {<BR><BR>List list = document.selectNodes( //foo/bar );<BR><BR>Node node = document.selectSingleNode(//foo/bar/author);<BR><BR>String name = node.valueOf( @name );<BR><BR>}<BR><BR><BR><BR><BR>例如，如果你想查找XHTML文档中所有的超链接，下面的代码可以实现： <BR><BR><BR><BR>public void findLinks(Document document) throws DocumentException {<BR><BR>List list = document.selectNodes( //a/@href );<BR><BR>for (Iterator iter = list.iterator(); iter.hasNext(); ) {<BR><BR>Attribute attribute = (Attribute) iter.next();<BR><BR>String url = attribute.getValue();<BR><BR>}<BR><BR>}<BR><BR><BR><BR><BR>5. 字符串与XML的转换<BR><BR>有时候经常要用到字符串转换为XML或反之，<BR><BR><BR>// XML转字符串 <BR>　 Document document = ...;<BR><BR>String text = document.asXML();<BR><BR>// 字符串转XML<BR><BR>String text = &lt;person&gt; &lt;name&gt;James&lt;/name&gt; &lt;/person&gt;;<BR><BR>Document document = DocumentHelper.parseText(text);<BR><BR><BR><BR><BR>6 用XSLT转换XML<BR><BR><BR><BR>public Document styleDocument(<BR><BR>Document document, <BR><BR>String stylesheet<BR><BR>) throws Exception {<BR><BR>// load the transformer using JAXP<BR><BR>TransformerFactory factory = TransformerFactory.newInstance();<BR><BR>Transformer transformer = factory.newTransformer( <BR><BR>new StreamSource( stylesheet ) <BR><BR>);<BR><BR>// now lets style the given document<BR><BR>DocumentSource source = new DocumentSource( document );<BR><BR>DocumentResult result = new DocumentResult();<BR><BR>transformer.transform( source, result );<BR><BR>// return the transformed document<BR><BR>Document transformedDoc = result.getDocument();<BR><BR>return transformedDoc;<BR><BR>}<BR><BR><BR><BR><BR>7. 创建XML<BR><BR>一般创建XML是写文件前的工作，这就像StringBuffer一样容易。<BR><BR><BR><BR>public Document createDocument() {<BR><BR>Document document = DocumentHelper.createDocument();<BR><BR>Element root = document.addElement(root);<BR><BR>Element author1 =<BR><BR>root<BR><BR>.addElement(author)<BR><BR>.addAttribute(name, James)<BR><BR>.addAttribute(location, UK)<BR><BR>.addText(James Strachan);<BR><BR>Element author2 =<BR><BR>root<BR><BR>.addElement(author)<BR><BR>.addAttribute(name, Bob)<BR><BR>.addAttribute(location, US)<BR><BR>.addText(Bob McWhirter);<BR><BR>return document;<BR><BR>}<BR><BR><BR><BR><BR>8. 文件输出<BR><BR>一个简单的输出方法是将一个Document或任何的Node通过write方法输出<BR><BR><BR>FileWriter out = new FileWriter( foo.xml );<BR><BR>document.write(out);<BR><BR><BR><BR><BR>如果你想改变输出的格式，比如美化输出或缩减格式，可以用XMLWriter类<BR><BR>public void write(Document document) throws IOException {<BR><BR>// 指定文件<BR><BR>XMLWriter writer = new XMLWriter(<BR><BR>new FileWriter( output.xml )<BR><BR>);<BR><BR>writer.write( document );<BR><BR>writer.close();<BR><BR>// 美化格式<BR><BR>OutputFormat format = OutputFormat.createPrettyPrint();<BR><BR>writer = new XMLWriter( System.out, format );<BR><BR>writer.write( document );<BR><BR>// 缩减格式<BR><BR>format = OutputFormat.createCompactFormat();<BR><BR>writer = new XMLWriter( System.out, format );<BR><BR>writer.write( document );<BR><BR>}<BR><BR><BR><BR><BR>如何，DOM4J够简单吧，当然，还有一些复杂的应用没有提到，如ElementHandler等。如果你动心了，那就一起来用DOM4J.<BR><BR>DOM4J官方网站：(我老连不上)<BR><BR></FONT><A href="http://www.dom4j.org/" target=_blank><STRONG><FONT face=Arial>http://www.dom4j.org</FONT></STRONG></A><BR><BR><FONT face=Arial>DOM4J下载(SourceForge)，最新版本为1.4<BR><BR></FONT><A href="http://sourceforge.net/projects/dom4j" target=_blank><STRONG><FONT face=Arial>http://sourceforge.net/projects/dom4j</FONT></STRONG></A><BR><BR><FONT face=Arial>参考资料：<BR><BR>DOM4J文档<BR><BR>Java 中的 XML：文档模型，第一部分：性能<BR><BR></FONT><A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml" target=_blank><STRONG><FONT face=Arial>http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml</FONT></STRONG></A><BR><BR><FONT face=Arial>Java 中的 XML：Java 文档模型的用法<BR><BR></FONT><A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava2/index.shtml" target=_blank><STRONG><FONT face=Arial>http://www-900.ibm.com/developerWorks/cn/xml/x-injava2/index.shtml</FONT></STRONG></A><BR><BR><FONT face=Arial>Java XML API 漫谈　by robbin<BR><BR></FONT><A href="http://www.hibernate.org.cn:8000/137.html" target=_blank><STRONG><FONT face=Arial>http://www.hibernate.org.cn:8000/137.html</FONT></STRONG></A><BR></P>
<P>&nbsp;</P><img src ="http://www.blogjava.net/jackybu/aggbug/2359.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-23 13:12 <a href="http://www.blogjava.net/jackybu/articles/2359.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 中的 XML：Java 文档模型的用法</title><link>http://www.blogjava.net/jackybu/articles/2358.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 05:11:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2358.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2358.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2358.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2358.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2358.html</trackback:ping><description><![CDATA[<A name=author1><SPAN class=atitle2>关于作者</SPAN><BR><IMG height=80 alt="作者照片 — Dennis Sosnosky" src="http://www-900.ibm.com/developerWorks/cn/i/p-sosnoski.jpg" width=64 align=left border=0> Dennis Sosnoski（<A href="mailto:dms@sosnoski.com"><FONT color=#002c99>dms@sosnoski.com</FONT></A>）是西雅图地区 Java 咨询公司 <A href="http://www.sosnoski.com/"><FONT color=#002c99>Sosnoski Software Solutions, Inc.</FONT></A> 的共同创始人和首席顾问。他已经有 30 多年专业软件开发经历，最近几年他一直关注服务器端的 Java 技术，包括 servlet、Enterprise JavaBean 和 XML。他已经发表了 Java 性能问题和常规服务器端 Java 技术的许多文章，而且担任 <A href="http://www.sosnoski.com/jxml"><FONT color=#002c99>Seattle Java-XML SIG</FONT></A> 的总裁。</A><img src ="http://www.blogjava.net/jackybu/aggbug/2358.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-23 13:11 <a href="http://www.blogjava.net/jackybu/articles/2358.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在 Java 中使用 DOM 和 XPath 进行有效的 XML 处理</title><link>http://www.blogjava.net/jackybu/articles/2357.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 05:10:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2357.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2357.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2357.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2357.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2357.html</trackback:ping><description><![CDATA[首席软件架构设计师, VelociGen Inc.<BR>2001 年 12 月
<BLOCKQUOTE>在对几个大型 XML 项目分析的基础上，本文探讨了在 Java 中如何有效和高效的使用 DOM。DOM 为创建、处理和操纵 XML 文档提供了灵活和有效的方法，但使用起来可能比较困难并且可能会导致不稳定和错误的代码。作者 Parand Tony Daruger 提供了一套 Java 用法模式和函数库，使 DOM 变得健壮且易于使用。</BLOCKQUOTE>
<P>文档对象模型（Document Object Model，DOM）是公认的 W3C 标准，它被用于与平台及语言无关的 XML 文档内容、结构和样式的动态访问和更新。它为表示文档定义了一套标准的接口集，也为访问和操纵文档定义了一套标准的方法。DOM 得到广泛的支持和普及，并且它以各种不同的语言实现，包括 Java、Perl、C、C++、VB、Tcl 和 Python。</P>
<P>正如我将在本文所演示的，当基于流的模型（例如 SAX）不能满足 XML 处理要求时，DOM 是一个极佳的选择。不幸的是，规范的几个方面，例如其语言无关性接口和“一切都是节点（everything-is-a-node）”抽象概念的使用，使其难以使用且易于生成脆弱代码。这在最近的几个大型 DOM 项目的研究中尤其明显，这些项目是由许多开发人员过去一年所创建的。下面讨论了常见的问题及其补救措施。</P>
<P><A id=1 name=1><SPAN class=atitle2>文档对象模型</SPAN></A><BR>DOM 规范被设计成可与任何编程语言一起使用。因此，它尝试使用在所有语言中都可用的一组通用的、核心的功能部件。DOM 规范同样尝试保持其接口定义方面的无关性。这就允许 Java 程序员在使用 Visual Basic 或 Perl 时应用他们的 DOM 知识，反之亦然。</P>
<P>该规范同样将文档的每个部分看成由类型和值组成的节点。这为处理文档的所有方面提供了完美的概念性框架。例如，下面的 XML 片段</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE>

<CODE>the Italicized portion.</CODE>



</PRE></TD></TR></TBODY></TABLE>
<P>就是通过以下的 DOM 结构表示的：</P>
<P><A id=figure1 name=figure1><B>图 1：XML 文档的 DOM 表示</B></A><BR><IMG height=232 alt="DOM 表示" src="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/dom.gif" width=385></P>
<P>树的每个 <CODE>Document</CODE>、<CODE>Element</CODE>、<CODE>Text</CODE> 和 <CODE>Attr</CODE> 部分都是 DOM <CODE>Node</CODE>。</P>
<P>完美的抽象确实付出了代价。考虑 XML 片段：<CODE>Value</CODE>。您或许会认为文本的值可以通过普通的 Java <CODE>String</CODE> 对象来表示，并且通过简单的 <CODE>getValue</CODE> 调用可访问。实际上，文本被当成 <CODE>tagname</CODE> 节点下的一个或多个子 <CODE>Node</CODE>。因此，为了获取文本值，您需要遍历 <CODE>tagname</CODE> 的子节点，将每个值整理成一个字符串。这样做有充分的理由：<CODE>tagname</CODE> 可能包含其它嵌入的 XML 元素，在这种情况下获取其文本值没有多大意义。然而，在现实世界中，我们看到由于缺乏便利的函数导致频繁的编码错误占了 80％ 的情况，这样做的确有意义。</P>
<P><A id="" name=""><SPAN class=atitle3>设计问题</SPAN></A><BR>DOM 语言无关性的缺点是通常在每个编程语言中使用的一整套工作方法和模式不能被使用。例如，不能使用熟悉的 Java <CODE>new</CODE> 构造创建新的 <CODE>Element</CODE>，开发者必须使用工厂构造器方法。<CODE>Node</CODE> 的集合被表示成 <CODE>NodeList</CODE>，而不是通常的 <CODE>List</CODE> 或 <CODE>Iterator</CODE> 对象。这些微小的不便意味着不同寻常的编码实践和增多的代码行，并且它们迫使程序员学习 DOM 的行事方法而不是用直觉的方法。</P>
<P>DOM 使用“一切都是节点”的抽象。这就意味着几乎 XML 文档的每个部分，例如：<CODE>Document</CODE>、<CODE>Element</CODE> 和 <CODE>Attr</CODE>，全都继承（<CODE>extend</CODE>）<CODE>Node</CODE> 接口。这不仅是概念上完美，而且还允许每个 DOM 的不同实现通过标准接口使其自身的类可见，并且没有通过中间包装类所导致的性能损失。</P>
<P>由于存在的节点类型数量及其访问方法缺乏一致性，“一切都是节点”的抽象丧失了一些意义。例如，<CODE> insertData</CODE> 方法被用来设置 <CODE>CharacterData</CODE> 节点的值，而通过使用 <CODE>setValue</CODE> 方法来设置 <CODE>Attr</CODE>（属性）节点的值。由于对于不同的节点存在不同的接口，模型的一致性和完美性降低了，而学习曲线增加了。</P>
<P><A id=2 name=2><SPAN class=atitle2>JDOM</SPAN></A><BR>JDOM 是使 DOM API 适应 Java 的研究计划，从而提供了更自然和易用的接口。由于认识到语言无关 DOM 构造的棘手本质，JDOM 目标在于使用内嵌的 Java 表示和对象，并且为常用任务提供便利的函数。</P>
<P>例如，JDOM 直接处理“一切都是节点”和 DOM 特定构造的使用（如 <CODE>NodeList</CODE>）。JDOM 将不同的节点类型（如 <CODE>Document</CODE>、<CODE>Element</CODE> 和 <CODE>Attribute</CODE>）定义为不同的 Java 类，这意味着开发者可以使用 <CODE>new</CODE> 构造它们，避免频繁类型转换的需要。JDOM 将字符串表示成 Java <CODE>String</CODE>，并且通过普通的 <CODE>List</CODE> 和 <CODE>Iterator</CODE> 类来表示节点的集合。（JDOM 用其本身类替代 DOM 类。）</P>
<P>JDOM 为提供更完善的接口做了相当有益的工作。它已经被接受成为 JSR（正式的 Java Specification Request），而且它将来很可能会被包含到核心的 Java 平台中。但是，因其还不是核心 Java API 的一部分，一些人对于使用它还心存犹豫。这儿还有关于与 Iterator 和 Java 对象频繁创建相关的性能问题的报告。（请参阅<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/index.shtml#resources"><FONT color=#002c99>参考资料</FONT></A>）。</P>
<P>如果您对 JDOM 的接受性和可用性已经满足，并且如果您也没有将 Java 代码和程序员转移到其它语言的直接需求，JDOM 是个值得探索的好选择。JDOM 还不能满足本文探讨的项目所在的公司需要，因而他们使用了非常普遍的 DOM。本文也是这样做的。</P>
<P><A id=common name=common><SPAN class=atitle2>常见编码问题</SPAN></A><BR>几个大型 XML 项目分析揭示了使用 DOM 中的一些常见问题。下面对其中的几个进行介绍。</P>
<P><A id="" name=""><SPAN class=atitle3>代码臃肿</SPAN></A><BR>在我们研究中查看的所有项目，本身都出现一个突出的问题：花费许多行代码行来做简单的事情。在某个示例中，使用 16 行代码检查一个属性的值。而同样的任务，带有改进的健壮性和出错处理，可以使用 3 行代码实现。DOM API 的低级本质、方法和编程模式的不正确应用以及缺乏完整 API 的知识，都会致使代码行数增加。下面的摘要介绍了关于这些问题的特定实例。</P>
<P><A id="" name=""><SPAN class=atitle3>遍历 DOM</SPAN></A><BR>在我们探讨的代码中，最常见的任务是遍历或搜索 DOM。<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/listing1.html"><FONT color=#002c99>清单 1</FONT></A> 演示了需要在文档的 <CODE>config</CODE> 节里查找一个称为“header”节点的浓缩版本代码：</P>
<P>清单 1 中，从根开始通过检索顶端元素遍历文档，获取其第一个子节点（<CODE>configNode</CODE>），并且最终单独检查 <CODE>configNode</CODE> 的子节点。不幸的是，这种方法不仅冗长，而且还伴随着脆弱性和潜在的错误。</P>
<P>例如，第二行代码通过使用 <CODE>getFirstChild</CODE> 方法获取中间的 <CODE>config</CODE> 节点。已经存在许多潜在的问题。根节点的第一个子节点实际上可能并不是用户正在搜索的节点。由于盲目地跟随第一个子节点，我忽视了标记的实际名称并且可能搜索不正确的文档部分。当源 XML 文档的根节点后包含空格或回车时，这种情况中发生一个频繁的错误；根节点的第一个子节点实际是 <CODE>Node.TEXT_NODE</CODE> 节点，而不是所希望的元素节点。您可以自己试验一下，从<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/index.shtml#resources"><FONT color=#002c99>参考资料</FONT></A>下载样本代码并且编辑 sample.xml 文件 — 在 <CODE>sample</CODE> 和 <CODE>config</CODE> 标记之间放置一个回车。代码立即异常而终止。要正确浏览所希望的节点，需要检查每个 <CODE>root</CODE> 的子节点，直到找到非 <CODE>Text</CODE> 的节点，并且那个节点有我正在查找的名称为止。</P>
<P>清单 1 还忽视了文档结构可能与我们期望有所不同的可能性。例如，如果 <CODE>root</CODE> 没有任何子节点，<CODE>configNode</CODE> 将会被设置为 <CODE>null</CODE>，并且示例的第三行将产生一个错误。因此，要正确浏览文档，不仅要单独检查每个子节点以及核对相应的名称，而且每步都得检查以确保每个方法调用返回的是一个有效值。编写能够处理任意输入的健壮、无错的代码，不仅需要非常关注细节，而且需要编写很多行代码。</P>
<P>最终，如果最初的开发者了解它的话，清单 1 中示例的所有功能应该可以通过利用对 <CODE>getElementsByTagName</CODE> 函数的简单调用实现。这是下面要讨论的。</P>
<P><A id="" name=""><SPAN class=atitle3>检索元素中的文本值</SPAN></A><BR>在所分析的项目中，DOM 遍历以后，第二项最常进行的任务是检索在元素中包含的文本值。考虑 XML 片段 <CODE>The Value</CODE>。如果已经导航到 <CODE>sometag</CODE> 节点，如何获取其文本值（<CODE>The Value</CODE>）呢？一个直观的实现可能是：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE>

<CODE>sometagElement.getData();</CODE>



</PRE></TD></TR></TBODY></TABLE>
<P>正如您所猜测到的，上面的代码并不会执行我们希望的动作。由于实际的文本被存储为一个或多个子节点，因此不能对 <CODE>sometag</CODE> 元素调用 <CODE>getData</CODE> 或类似的函数。更好的方法可能是：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE>

<CODE>sometag.getFirstChild().getData();</CODE>



</PRE></TD></TR></TBODY></TABLE>
<P>第二种尝试的问题在于值实际上可能并不包含在第一个子节点中；在 <CODE>sometag</CODE> 内可能会发现处理指令或其它嵌入的节点，或是文本值包含在几个子节点而不是单单一个子节点中。考虑到空格经常作为文本节点表示，因此对 <CODE>sometag.getFirstChild()</CODE> 的调用可能仅让您得到标记和值之间的回车。实际上，您需要遍历所有子节点，以核对 <CODE>Node.TEXT_NODE</CODE> 类型的节点，并且整理它们的值直到有完整的值为止。</P>
<P>注意，JDOM 已经利用便利的函数 <CODE>getText</CODE> 为我们解决了这个问题。DOM 级别 3 也将有一个使用规划的 <CODE>getTextContent</CODE> 方法的解答。教训：尽可能使用较高级的 API 是不会错的。</P>
<P><A id="" name=""><SPAN class=atitle3>getElementsByTagName</SPAN></A><BR>DOM 级别 2 接口包含一个查找给定名称的子节点的方法。例如，调用：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE>

<CODE>NodeList names = someElement.getElementsByTagName("name");



</CODE>



</PRE></TD></TR></TBODY></TABLE>
<P>将返回一个包含在 <CODE>someElement</CODE> 节点中称为 <CODE>names</CODE> 的节点 <CODE>NodeList</CODE>。这无疑比我所讨论的遍历方法更方便。这也是一组常见错误的原因。</P>
<P>问题在于 <CODE>getElementsByTagName</CODE> 递归地遍历文档，从而返回所有匹配的节点。假定您有一个包含客户信息、公司信息和产品信息的文档。所有这三个项中都可能含有 <CODE>name</CODE> 标记。如果调用 <CODE>getElementsByTagName</CODE> 搜索客户名称，您的程序极有可能行为失常，除了检索出客户名称，还会检索出产品和公司名称。在文档的子树上调用该函数可能会降低风险，但由于 XML 的灵活本质，使确保您所操作的子树包含您期望的结构，且没有您正在搜索的名称的虚假子节点就变得十分困难。</P>
<P><A id=3 name=3><SPAN class=atitle2>DOM 的有效使用</SPAN></A><BR>考虑到由 DOM 设计强加的限制，如何才能有效和高效的使用该规范呢？下面是使用 DOM 的几条基本原则和方针，以及使工作更方便的函数库。</P>
<P><A id="" name=""><SPAN class=atitle3>基本原则</SPAN></A><BR>如果您遵循几条基本原则，您使用 DOM 的经验将会显著提高：</P>
<UL>
<LI>不要使用 DOM 遍历文档。 
<LI>尽可能使用 XPath 来查找节点或遍历文档。 
<LI>使用较高级的函数库来更方便地使用 DOM。</LI></UL><BR><BR>
<P>这些原则直接从对常见问题的研究中得到。正如上面所讨论的，DOM 遍历是出错的主要原因。但它也是最常需要的功能之一。如何通过不使用 DOM 而遍历文档呢？</P>
<P><A id="" name=""><SPAN class=atitle3>XPath</SPAN></A><BR>XPath 是寻址、搜索和匹配文档的各个部分的语言。它是 W3C 推荐标准（Recommendation），并且在大多数语言和 XML 包中实现。您的 DOM 包可能直接支持 XPath 或通过加载件（add-on）支持。本文的样本代码对于 XPath 支持使用 Xalan 包。</P>
<P>XPath 使用路径标记法来指定和匹配文档的各个部分，该标记法与文件系统和 URL 中使用的类似。例如，XPath:<CODE>/x/y/z</CODE> 搜索文档的根节点 <CODE>x</CODE>，其下存在节点 <CODE>y</CODE>，其下存在节点 <CODE>z</CODE>。该语句返回与指定路径结构匹配的所有节点。</P>
<P>更为复杂的匹配可能同时在包含文档的结构方面以及在节点及其属性的值中。语句 <CODE>/x/y/*</CODE> 返回父节点为 <CODE>x</CODE> 的 <CODE>y</CODE> 节点下的任何节点。<CODE>/x/y[@name='a']</CODE> 匹配所有父节点为 <CODE>x</CODE> 的 <CODE>y</CODE> 节点，其属性称为 <CODE>name</CODE>，属性值为 <CODE>a</CODE>。请注意，XPath 处理筛选空格文本节点以获得实际的元素节点 — 它只返回元素节点。</P>
<P>详细探讨 XPath 及其用法超出了本文的范围。请参阅<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/index.shtml#resources"><FONT color=#002c99>参考资料</FONT></A>获得一些优秀教程的链接。花点时间学习 XPath，您将会更方便的处理 XML 文档。</P>
<P><A id=library name=library><SPAN class=atitle2>函数库</SPAN></A><BR>当研究 DOM 项目时令我们惊奇的一个发现，是存在的拷贝和粘贴代码的数量。为什么有经验的开发者没有使用良好的编程习惯，却使用拷贝和粘贴方法而不是创建助手（helper）库呢？我们相信这是由于 DOM 的复杂性加深了学习的难度，并使开发者要理解能完成他们所需要的第一段代码。开发产生构成助手库规范的函数所需的专门技术需要花费大量的时间。</P>
<P>要节省一些走弯路的时间，这里是一些将使您自己的库可以运转起来的基本助手函数。</P>
<P><A id="" name=""><SPAN class=atitle3>findValue</SPAN></A><BR>使用 XML 文档时，最常执行的操作是查找给定节点的值。正如上所讨论的，在遍历文档以查找期望的值和检索节点的值中都出现难度。可以通过使用 XPath 来简化遍历，而值的检索可以一次编码然后重用。在两个较低级函数的帮助下，我们实现了 <CODE>getValue</CODE> 函数，这两个低级函数是：由 Xalan 包提供的 <CODE>XPathAPI.selectSingleNode</CODE>（用来查找和返回与给定的 XPath 表达式匹配的第一个节点）；以及 <CODE>getTextContents</CODE>，它非递归地返回包含在节点中的连续文本值。请注意，JDOM 的 <CODE>getText</CODE> 函数，或将出现在 DOM 级别 3 中规划的 <CODE>getTextContent</CODE> 方法，都可用来代替 <CODE>getTextContents</CODE>。<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/listing2.html"><FONT color=#002c99>清单 2</FONT></A> 包含了一个简化的清单；您可以通过下载样本代码来访问所有函数（请参阅<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/index.shtml#resources"><FONT color=#002c99>参考资料</FONT></A>）。</P>
<P>通过同时传入要开始搜索的节点和指定要搜索节点的 XPath 语句来调用 <CODE>findValue</CODE>。函数查找第一个与给定 XPath 匹配的节点，并且抽取其文本值。</P>
<P><A id="" name=""><SPAN class=atitle3>setValue</SPAN></A><BR>另一项常用的操作是将节点的值设置为希望的值，如<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/listing3.html"><FONT color=#002c99>清单 3</FONT></A> 所示。该函数获取一个起始节点和一条 XPath 语句 — 就象 <CODE>findValue</CODE> — 以及一个用来设置匹配的节点值的字符串。它查找希望的节点，除去其所有子节点（因此除去包含在其中的任何文本和其它元素），并将其文本内容设置为传入的（passed-in）字符串。</P>
<P><A id="" name=""><SPAN class=atitle3>appendNode</SPAN></A><BR>虽然某些程序查找和修改包含在 XML 文档中的值，而另一些则通过添加和除去节点来修改文档本身的结构。这个助手函数简化了文档节点的添加，如<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/listing4.html"><FONT color=#002c99>清单 4</FONT></A> 所示。</P>
<P>该函数的参数有：要将新节点添加到其下的节点，要添加的新节点名称，以及指定要将节点添加到其下位置的 XPath 语句（也就是，新节点的父节点应当是哪个）。新节点被添加到文档的指定位置。</P>
<P><A id=analysis name=analysis><SPAN class=atitle2>最终分析</SPAN></A><BR>DOM 的语言无关性设计为其带来了非常广泛的可应用性并使其在大量的系统和平台上得以实现。这样做的代价是：使 DOM 比为每个语言专门设计的 API 更困难且更缺乏直观性。</P>
<P>DOM 奠定了一个非常有效的基础，遵循一些简单的原则就可其上构建易于使用的系统。凝结了一大群用户智慧和经验的 DOM 未来版本正在设计之中，而且极有可能为这里讨论的问题提供解决方案。如 JDOM 这样的项目正在修改该 API 以获得更自然 Java 感觉，而且如本文中所述的技术可以帮助您使 XML 的操纵更方便、更简洁并且不易出错。利用这些项目且遵循这些用法模式以允许 DOM 成为基于 XML 项目的出色平台。</P>
<P><A id=resources name=resources><SPAN class=atitle2>参考资料</SPAN></A></P>
<UL>
<LI>在<A href="javascript:void forumWindow()"><FONT color=#002c99>论坛</FONT></A>参与有关本文的讨论。 
<LI>要获取有关的背景知识，参考由 W3C 维护的 <A href="http://www.w3.org/DOM/"><FONT color=#002c99>DOM 规范</FONT></A>。 
<LI>下载和测试来自本文的<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-domjava/edom.jar"><FONT color=#002c99>样本代码</FONT></A>。要运行代码，在您的 classpath 中需要 Xerces 和 Xalan。 
<LI>从 <A href="http://xml.apache.org/"><FONT color=#002c99>Apache 项目</FONT></A>下载 Xerces 和 Xalan DOM 以及 XPath 实现包。 
<LI>使用 developerWorks 教程<A href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/xml-onlinecourse-bytitle/FA1F4B1ED76D75A848256B22001D9D29?OpenDocument"><FONT color=#002c99> 理解 DOM</FONT></A> 获得 DOM 的深入理解。 
<LI>核对 <A href="http://www.jdom.org/"><FONT color=#002c99>JDOM 项目</FONT></A>。 
<LI>使用 developerWorks 文章 <A href="http://www-900.ibm.com/developerWorks/cn/java/j-jdom/index.shtml"><FONT color=#002c99>Simplify XML programming with JDOM</FONT></A> 学习 JDOM 的使用技术。 
<LI>使用 <A href="http://www.zvon.org/xxl/XPathTutorial/General/examples.html"><FONT color=#002c99>Zvon XPath Tutorial</FONT></A> 加速 XPath 学习。 
<LI>研究由 W3C 维护的 <A href="http://www.w3.org/TR/xpath"><FONT color=#002c99>XPath 规范</FONT></A>。 
<LI>如果您想了解 IBM 的 WebSphere Application Server（WAS）是如何支持 XML 开发的，请参阅 <A href="http://www-106.ibm.com/developerWorks/cgi-bin/click.cgi?url=www-4.ibm.com/software/webservers/appserv/doc/v35/ae/infocenter/was/040203.html&amp;origin=x"><FONT color=#002c99>WAS 高级版 3.5 在线帮助中有关 XML 的技术背景信息</FONT></A>。 
<LI><A href="http://www-4.ibm.com/software/webservers/studio/preregister.html"><FONT color=#002c99>WebSphere Studio Application Developer</FONT></A> 包含一个针对 XML 和 Java 的集成可视开发环境。在 <A href="http://www7b.boulder.ibm.com/wsdd/zones/studio/index.html"><FONT color=#002c99>WebSphere Developer Domain Studio 专区</FONT></A>上获取更多信息。</LI></UL><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A id=author1 name=author1><SPAN class=atitle2>关于作者</SPAN><BR><IMG height=80 alt="作者照片：Parand Tony Darugar" src="http://www-900.ibm.com/developerWorks/cn/i/authors/p-darugar2.jpg" width=64 align=left border=0>Parand Tony Darugar 是 VelociGen Inc.的联合创始人和首席软件架构设计师，VelociGen Inc.是一个 Web 服务的软件平台供应商。他的兴趣包括用于电子商务集成的高性能系统、智能分布式体系结构、神经网络和人工智能。可以通过 <A href="mailto:tdarugar@velocigen.com"><FONT color=#002c99>tdarugar@velocigen.com</FONT></A></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/jackybu/aggbug/2357.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-23 13:10 <a href="http://www.blogjava.net/jackybu/articles/2357.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 中的 XML：文档模型，第一部分：性能</title><link>http://www.blogjava.net/jackybu/articles/2356.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 05:09:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2356.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2356.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2356.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2356.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2356.html</trackback:ping><description><![CDATA[<BLOCKQUOTE>在本文中，Java 顾问 Dennis Sosnoski 比较几个 Java 文档模型的性能和功能。当选择模型时，无法做到每次都权衡得很清楚，如果以后改变主意，则需要大量编码来进行切换。作者将性能结果放入特性集合的上下文中并遵循标准，对所要求的正确选择给出了一些建议。本文包含用于这组测试的几张图表和源代码。</BLOCKQUOTE>
<P>使用内存中 XML 文档的 Java 开发者可以选择使用标准 DOM 表示或几个 Java 特定模型中的任何一个。该灵活性已经帮助将 Java 建立成 XML 工作的出色平台。但是，由于不同模型数量的增加，已经更加难以确定如何比较模型的功能、性能和易用性。</P>
<P>关于使用“Java 中的 XML”系列中的第一篇文章研究了 Java 中一些领先的 XML 文档模型的特性和性能。它包括一组性能测试的结果(带有可下载的测试代码，请参阅<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml#resources">参考资料</A>）。在系列中的第二篇文章将通过比较为实现同样任务所使用的不同模型的样本代码来研究易用性问题。</P>
<P><A id=1 name=1><SPAN class=atitle2>文档模型</SPAN></A><BR>Java 中的可用文档模型数一直在增加。对于本文，我已经涵盖了最常用的模型和几项选择，这演示了那些可能还未被广泛了解或使用的特别令人感兴趣的特性。随着“XML 名称空间”的重要性增加，我已经包含了仅支持该功能的模型。下面列出了带有简要介绍和版本信息的模型。</P>
<P>仅为说明本文中所使用的术语：</P>
<UL>
<LI><I>解析器</I>是指解释 XML 文本文档结构的程序 
<LI><I>文档表示</I>是指程序用于内存中文档的数据结构 
<LI><I>文档模型</I>是指支持使用文档表示的库和 API</LI></UL><BR><BR>
<P>某些 XML 应用程序根本不需要使用文档模型。如果应用程序可以通过文档的一次遍历搜集它需要的信息，则可能直接使用解析器。该方法可能需要增加一些工作量，但是它的性能总是优于在内存中构建文档表示。</P>
<P><A id=2 name=2><SPAN class=atitle3>DOM</SPAN></A><BR>DOM（“文档对象模型”）是用与平台和语言无关的方式表示 XML 文档的官方 W3C 标准。对于任何 Java 特定的模型，它是很好的对照。为了值得与 DOM 标准分开，Java 特定模型应该提供比 Java DOM 实现更优越的性能和／或易用性的优势。</P>
<P>DOM 定义充分利用了 XML 文档不同组件的接口和继承性。这为开发者带来了将公共接口用于几个不同类型组件的优势，但是同时增加了 API 的复杂性。因为 DOM 是与语言无关的，所以接口不需要利用公共 Java 组件，例如，Collections 类。</P>
<P>本文涉及两个 DOM 实现：Crimson 和 Xerces Java。Crimson 是基于 Sun Project X 解析器的 Apache 项目。它合并一个包含 DTD 支持的完整验证解析器。可以通过 SAX2 接口访问该解析器，并且 DOM 实现可以与其它 SAX2 解析器一起工作。Crimson 是在 Apache 许可证下发布的开放源码。用于性能比较的版本是 Crimson 1.1.1（jar 文件大小是 0.2MB），它包含有用于从文本文件的 DOM 构建的 SAX2 解析器。</P>
<P>另一个测试的 DOM 实现，即 Xerces Java 是另一个 Apache 项目。初始时，Xerces 基于 IBM Java 解析器（通常称为 XML4J）。（当前还处于早期 beta 测试版的重新开发的 Xerces Java 2 将最终继承它。当前版本有时称为 Xerces Java 1。）如同使用 Crimson 一样，可以通过 SAX2 接口和 DOM 来访问 Xerces 解析器。然而，Xerces 不提供将 Xerces DOM 与不同的 SAX2 解析器一起使用的任何方法。Xerces Java 包含对 DTD 和 XML Schema 的验证支持（仅带有对 Schema 支持的最小限制）。</P>
<P>Xerces Java 还支持 DOM 的延迟节点扩展方式（请参考本文中的<I>延迟 Xerces</I> 或 <I>Xerces def.</I>），其中文档组件初始时是以压缩格式表示的，仅当使用时才将它扩展成完整的 DOM 表示。这种方式的用意是允许更快的解析并降低内存的使用，尤其对于那些可能仅使用部分输入文档的应用程序。与 Crimson 类似，Xerces 是在 Apache 许可证下发布的开放源码。用于性能比较的版本是 Xerces 1.4.2（jar 文件大小是 1.8MB）。</P>
<P><A id=3 name=3><SPAN class=atitle3>JDOM</SPAN></A><BR>JDOM 的目的是成为 Java 特定文档模型，它简化与 XML 的交互并且比使用 DOM 实现更快。由于是第一个 Java 特定模型，JDOM 一直得到大力推广和促进。正在考虑通过“Java 规范请求 JSR-102”将它最终用作“Java 标准扩展”。虽然实际将采用的格式仍在开发中，还是对两个 beta 测试版的 JDOM API 做了很大的更改，。从 2000 年初就已经开始了 JDOM 开发。</P>
<P>JDOM 与 DOM 主要有两方面不同。首先，JDOM 仅使用具体类而不使用接口。这在某些方面简化了 API，但是也限制了灵活性。第二，API 大量使用了 Collections 类，简化了那些已经熟悉这些类的 Java 开发者的使用。</P>
<P>JDOM 文档声明其目的是“使用 20%（或更少）的精力解决 80%（或更多）Java/XML 问题”（根据学习曲线假定为 20%）。JDOM 对于大多数 Java/XML 应用程序来说当然是有用的，并且大多数开发者发现 API 比 DOM 容易理解得多。JDOM 还包括对程序行为的相当广泛检查以防止用户做任何在 XML 中无意义的事。然而，它仍需要您充分理解 XML 以便做一些超出基本的工作（或者甚至理解某些情况下的错误）。这也许是比学习 DOM 或 JDOM 接口都更有意义的工作。</P>
<P>JDOM 自身不包含解析器。它通常使用 SAX2 解析器来解析和验证输入 XML 文档（尽管它还可以将以前构造的 DOM 表示作为输入）。它包含一些转换器以将 JDOM 表示输出成 SAX2 事件流、DOM 模型或 XML 文本文档。JDOM 是在 Apache 许可证变体下发布的开放源码。用于性能比较的版本是 JDOM Beta 0.7（jar 文件大小是 0.1MB）它带有用于从文本文件构建 JDOM 表示的 Crimson SAX2 解析器。</P>
<P><A id=4 name=4><SPAN class=atitle3>dom4j</SPAN></A><BR>虽然 dom4j 代表了完全独立的开发结果，但最初，它是 JDOM 的一种智能分支。它合并了许多超出基本 XML 文档表示的功能，包括集成的 XPath 支持、XML Schema 支持（当前为 alpha 格式）以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项，它通过 dom4j API 和标准 DOM 接口具有并行访问功能。从 2000 下半年开始，它就一直处于开发之中，保留了最近发行版之间的现有 API。</P>
<P>为支持所有这些功能，dom4j 使用接口和抽象基本类方法。dom4j 大量使用了 API 中的 Collections 类，但是在许多情况下，它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是，虽然 dom4j 付出了更复杂的 API 的代价，但是它提供了比 JDOM 大得多的灵活性。</P>
<P>在添加灵活性、XPath 集成和对大文档处理的目标时，dom4j 的目标与 JDOM 是一样的：针对 Java 开发者的易用性和直观操作。它还致力于成为比 JDOM 更完整的解决方案，实现在本质上处理所有 Java/XML 问题的目标。在完成该目标时，它比 JDOM 更少强调防止不正确的应用程序行为。</P>
<P>dom4j 使用相同方法作为 JDOM 输出，这依靠 SAX2 解析器输入处理，依靠转换器将输出处理成 SAX2 事件流、DOM 模型或 XML 文本文档。dom4j 是在 BSD 样式许可证下发布的开放源码，该许可证本质上等价于 Apache 样式许可证。用于性能比较的版本是 dom4j 0.9（jar 文件大小是 0.4MB），带有用于从文本文件构建表示的受绑定 AElfred SAX2 解析器（由于 SAX2 选项设置，测试文件之一无法由 dom4j 使用用于 JDOM 测试的同一 Crimson SAX2 解析器来处理）。</P>
<P><A id=5 name=5><SPAN class=atitle3>Electric XML</SPAN></A><BR>Electric XML（EXML）是支持分布式计算的商业项目的附属产物。它与目前为止讨论的其它模型的不同之处在于，它只能适当地支持 XML 文档的子集，它没有为验证提供任何支持并且有更严格的许可证。然而，EXML 的优势是大小很小并提供了对 XPath 子集的直接支持，因为在最近几篇文章中已经将它提升其它模型的替代模型，所以通过该比较使它成为一个引人注意的候选。</P>
<P>虽然 EXML 通过使用抽象的基本类方法取得了某些相同效果，但它在避免使用接口方面使用与 JDOM 类似的方法（主要区别是接口为扩展实现提供了更大的灵活性）。它与 JDOM 的不同之处还在于避免使用 Collections 类。该组合为其提供了非常简单的 API，在许多方面类似于带有附加 XPath 操作的 DOM API 简化版本。</P>
<P><I>仅</I>当空白与非空白文本内容邻近时，EXML 才在文档中保留空白，这就将 EXML 限制成 XML 文档的一个子集。标准 XML 需要在读取文档时保留该空白，除非对文档 DTD 可以确认有无空白无关紧要。对于事先已经知道空白无关紧要的许多 XML 应用程序来说，EXML 方法工作得很好，但是它防止对于期望保留空白的文档（例如，生成由浏览器显示或查看的文档的应用程序）使用 EXML。（有关作者对于该主题的谦虚建议，请参阅副栏<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml#whitespaceSB">使用空白的目的</A>。）</P>
<P>这种空白的删除会对性能比较产生误导效果 — 许多类型的测试范围与文档中的组件个数成比例，并且由 EXML 删除的每个空白序列都是其它模型中的组件。EXML 包含在本文显示的结果中，但是解释性能差异时请记住这种影响。</P>
<P>EXML 使用集成的解析器依据文本文档构建文档表示。除了通过文本方式外，它不提供从 DOM（或 SAX2）转换或转换成 SAX2（或 DOM）事件流的任何方式。EXML 是由 Mind Electric 在禁止将它嵌入某些类型的应用程序或库的受限许可证下发布的开放源码。用于性能比较的版本是 Electric XML 2.2（jar 文件大小是 0.05MB）。</P>
<P><A id=6 name=6><SPAN class=atitle3>XML Pull Parser</SPAN></A><BR>XML Pull Parser (XPP）是最近开发的，它演示了 XML 解析的不同方法。与 EXML 一样，XPP 只能适当支持 XML 文档的子集并且不提供验证的任何支持。它同样具有尺寸小的优势。这种优势再与拉回解析器方法结合，使它成为该比较中的良好替换项。</P>
<P>XPP 几乎独占地使用接口，但是它仅使用所有类中的一小部分。和 EXML 一样，XPP 避免使用 API 中的 Collections 类。总的来说，它是本文中最简单的文档模型 API。</P>
<P>将 XPP 限制成 XML 文档子集的局限性是它不支持文档中的实体、注释或处理指示信息。XPP 创建仅包含元素、属性（包括“名称空间”）和内容文本的文档结构。这对于某些类型的应用程序来说是一种非常严格的限制。但是通常它对性能的影响比 EXML 空白处理对性能的影响小。在本文中我仅使用了一个与 XPP 不兼容的测试文件，并且在带有注释的图表中显示了 XPP 结果，该注释不包含该文件。</P>
<P>XPP 中的拉回解析器支持（本文中称为 <I>XPP 拉回</I>）通过将解析实际上推迟到访问文档的一个组件时才进行，然后按照构造那个组件的需要对文档进行解析。该技术想实现允许非常快速的文档显示或分类应用，尤其在需要转发或除去（而不是对文档进行完全解析和处理）文档时。该方法的使用是可选的，如果以非拉回型方式使用 XPP，它对整个文档进行解析并且同时地构建完整的表示。</P>
<P>与 EXML 一样，XPP 使用依据文本文档构建文档表示的集成语法解析器，并且除了通过文本方式外，它不提供从 DOM（或 SAX2）转换或转换成 SAX2（或 DOM）事件流的任何方式。XPP 是具有 Apache 样式许可证的开放源代码。用于性能比较的版本是 PullParser 2.0.1 Beta 8（jar 文件大小是 0.04MB）。</P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerWorks/cn/i/bg-gold.gif><A id=testSB name=testSB><B>测试详细信息</B></A><BR>所显示的计时结果是来自使用 Sun Microsystems Java version 1.3.1、Java HotSpot Client VM 1.3.1-b24 测试，这些软件是运行在带有 256MB RAM 的 Athlon 1GHz 系统上的 Redhat Linux 7.1 下。将这些测试的初始 JVM 和最大内存大小都设置成 128MB，我想将它表示为服务器类型执行环境。 
<P>在使用初始缺省 JVM 内存设置为 2MB 和最大内存为 64MB 运行的测试中，带有较大 jar 文件大小（DOM、JDOM 和 dom4j）的模型的结果非常差，尤其在运行测试的平均时间中。这可能是由于内存受限执行的 HotSpot JVM 的无效操作引起的。</P>
<P>文档模型中的两种（XPP 和 EXML）支持直接将文档输入成“字符串”或字符数组。该类型直接输入不能代表实际应用程序，因此我在这些测试中避免使用它。对于输入和输出，我使用 Java 流封装字节以消除 I/O 对性能的影响，而保留了用于 XML 文档输入和输出的应用程序在典型情况下使用的语言接口。</P></TD></TR></TBODY></TABLE>
<P><A id=7 name=7><SPAN class=atitle2>性能比较</SPAN></A><BR>本文中使用的性能比较基于对一组选中的 XML 文档进行的解析和使用，这些文档试图代表较大范围的应用程序：</P>
<UL>
<LI>much_ado.xml，标记成 XML 的莎士比亚戏剧。没有属性并且是相当简单的结构（202K 字节）。 
<LI>periodic.xml, XML 中的元素的周期表。一些属性，也是相当简单的（117K 字节）。 
<LI>soap1.xml，取自规范的样本 SOAP 文档。大量名称空间和属性（0.4K 字节，每次测试需要重复 49 次）。 
<LI>soap2.xml，SOAP 文档格式中的值列表。大量名称空间和属性（134K 字节）。 
<LI>nt.xml，标记为 XML 的“新约”。没有属性并且非常简单的结构，大量文本内容（1047K 字节）。 
<LI>xml.xml，XML 规范，不带 DTD 引用，在内部定义所有实体。带有大量混合内容的文本样式标记，一些属性（160K 字节）。</LI></UL>
<P>关于测试平台的更多信息，请参阅副栏<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml#testSB">测试详细信息</A>并查看<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml#resources">参考资料</A>以获取用于测试的源代码的链接。</P>
<P>除了非常小的 soap1.xml 文档之外，所有评测时间都是指文档的每次特定测试所经历的时间。在 soap1.xml 的情况下，评测的时间是 49 个连续的文档测试（总数为 20K 字节文本的足够副本数）。</P>
<P>测试框架在一个文档上运行一个特定的测试多次（这里显示运行了 10 次），依此跟踪该测试的最短时间和平均时间，然后继续同一文档上的下一个测试。完成对一个文档的全部测试序列后，它对下一个文档重复该过程。为防止文档模型之间的交互，在执行每个测试框架时仅测试一个模型。</P>
<P>HotSpot 以及类似于动态优化 JVM 的计时基准程序是出了名的棘手的；测试序列中的小变化经常导致计时结果发生很大变化。我已经发现对于执行特定代码段的平均时间时，确实如此；最短时间比较一致，正是我在这些结果中列出的值。可以参阅第一次测试（文档构建时间）的<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/avgcomp.shtml" target=new>平均和最短时间</A>的比较。</P>
<P><A id=8 name=8><SPAN class=atitle3>文档构建时间</SPAN></A><BR>文档构建时间测试检查解析文本文档和构造文档表示所需的时间。出于比较目的，已经在图表中包含了使用 Crimson 和 Xerces SAX2 解析的 SAX2 解析时间，因为大多数文档模型（除了 EXML 和 XPP 外的所有文档）使用 SAX2 解析事件流作为文档构建过程的输入。图 1 描述了测试结果。</P>
<P><A id=figure1 name=figure1><B>图 1. 文档构建时间</B></A><BR><IMG height=608 alt=文档构建时间图 src="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/buildmintime.gif" width=600></P>
<P>对于大多数测试文档来说，XPP 拉回的构建时间太短以至于难以计算（因为在这种情况下，实际上没有对该文档进行解析），只显示为非常短的 soap1.xml。对于该文件，拉回解析器内存大小和相关的创建开销使 XPP 显得相对比较缓慢。这是因为测试程序为正在进行解析的文档的每个副本创建一个新的拉回解析器副本。在 soap1.xml 情况下，每次评测时间使用 49 个副本。分配与初始化这些解析器实例的开销大于重复解析文本并构建文档表示的大多数其它方法所需的时间。</P>
<P>XPP 的作者在一个电子邮件的讨论中指出，在实际应用程序中，可以合用拉回解析器实例以重新使用。如果这样做的话，soap1.xml 文件的开销将明显降到忽略不计程度。对于更大的文件，甚至不需要合用，拉回解析器创建开销也可以变得忽略不计。</P>
<P>在本测试中，XPP（带有完整解析），带有延迟节点创建的 Xerces 和 dom4j 都显示整体上的同等性能。延迟的 Xerces 对于较大的文档尤其出色，但是对于较小的文档的开销较高 — 甚至比常规 Xerces DOM 高很多。在第一次使用文档的一部分时，延迟节点创建方法的开销也较高，这会降低快速解析的优势。</P>
<P>对于较小的 soap1.xml 文件，所有格式（SAX2 解析、常规 DOM 和延迟 DOM）的 Xerces 的显得开销较高。对于该文件 XPP（完全解析）尤其出色，对于 soap1.xml，EXML 甚至超过基于 SAX2 的模型。虽然 EXML 具有废弃单独的空白内容的优势，但是总体上，它是本测试中最差的。</P>
<P><A id=9 name=9><SPAN class=atitle3>文档遍历时间</SPAN></A><BR>文档遍历时间测试检查遍历构造的文档表示所需的时间，按文档顺序遍历每个元素、属性和文本内容段。它试图表示文档模型接口的性能，这对于从进行过解析的文档中重复访问信息的应用程序来说可能很重要。总体上，遍历时间比解析时间快得多。对于只对解析过的文档单次遍历的应用程序，解析时间将比遍历时间更重要。图 2 显示了结果。</P>
<P><A id=figure2 name=figure2><B>图 2. 文档遍历时间</B></A><BR><IMG height=495 alt=文档遍历时间表 src="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/walkmintime.gif" width=600></P>
<P>在本测试中，XPP 的性能大大超过了其余的测试对象。Xerces DOM 所花费的时间大约是 XPP 的两倍。虽然 EXML 具有废弃文档中单独的空白内容的优势，但是，EXML 花费的时间几乎是 XPP 的三倍。dom4j 在这张图中处于中间位置。</P>
<P>使用 XPP 拉回时，直到访问文档表示时才真正发生对文档文本的解析。这导致第一次遍历文档表示时的开销非常大（表中未显示）。如果以后访问整个文档表示，则当使用拉回解析方法时，XPP 显示性能的净损失。对于拉回解析器来说，前两个测试所需的总时间比使用 XPP 的常规解析长（长 20% 到 100%，这取决于文档）。但是，当还未完全访问正在进行解析的文档时，拉回解析器方法仍然具有可观的性能优势。</P>
<P>带有延迟节点创建的 Xerces 显示了相似的行为，第一次访问文档表示时导致性能下降（图中未显示）。但是，在 Xerces 情况下，节点创建开销大约与解析期间常规 DOM 创建的性能差值相同。对于较大的文档来说，用 Xerces 延迟的前两次测试所需的总时间大致与使用带常规 DOM 构建的 Xerces 所用的时间相同。如果在非常大的文档（可能 10KB 或更大）上使用 Xerces，则延迟的节点创建似乎是一个好选择。</P>
<P><A id=10 name=10><SPAN class=atitle3>文档修改时间</SPAN></A><BR>这个测试检查系统地修改构造文档表示所需的时间，其结果在<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml#figure3">图 3</A> 中显示。它遍历表示，删除所有单独的空白内容并且用新添加的元素封装每个非空白内容字符串。它还向包含非空白内容的原始文档的每个元素中添加一个属性。该测试试图表示经过一定范围文档修改后文档模型的性能。如遍历时间一样，修改时间比解析时间短很多。因此，对于仅单次遍历每个解析过的文档的应用程序来说，解析时间将更重要。</P>
<P><A id=figure3 name=figure3><B>图 3. 文档修改时间</B></A><BR><IMG height=507 alt=文档修改时间表 src="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/modmintime.gif" width=600></P>
<P>这次测试中 EXML 处于领先地位，但是由于在解析期间它总是废弃单独的空白内容，它才比其它模型具有性能上的优势。这意味着在测试期间没有要从 EXML 表示中进行删除的内容。</P>
<P>在修改性能方面，XPP 仅次于 EXML，并且与 EXML 不同，XPP 测试包含删除。Xerces DOM 和 dom4j 接近地处于中间位置，JDOM 和 Crimson DOM 模型的性能仍是最差。</P>
<P><A id=11 name=11><SPAN class=atitle3>文本生成时间</SPAN></A><BR>这个测试检查将文档表示输出成文本 XML 文档所需的时间；结果显示在<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml#figure4">图 4</A> 中。对于不专门使用 XML 文档的任何应用程序，该步骤似乎是整体性能的一个重要部分，特别是因为将文档输出为文本所需的时间总体接近于对文档输入进行解析所需的时间。为使这些时间具有直接的可比性，该测试使用原始文档，而没有使用由前面的测试所生成的已修改文档。</P>
<P><A id=figure4 name=figure4><B>图 4. 文本生成时间</B></A><BR><IMG height=507 alt=文本生成时间表 src="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/textmintime.gif" width=600></P>
<P>文本生成时间测试表明各模型之间的差别小于前面测试中各项的差别，Xerces DOM 性能最好，但领先不多，JDOM 性能最差。EXML 的性能优于 JDOM，但是这同样是由于 EXML 废弃空白内容。</P>
<P>许多模型提供了控制文本输出格式的选项，并且一些选项似乎影响文本生成时间。这个测试只使用每个模型的最基本的输出格式，因此结果只显示缺省性能而不显示最好的可能性能。</P>
<P><A id=12 name=12><SPAN class=atitle3>文档内存大小</SPAN></A><BR>这个测试检查用于文档表示的内存空间。这对于使用大文档或同时使用多个较小文档的开发者来说，意义尤为重要。图 5 显示这个测试的结果。</P>
<P><A id=figure5 name=figure5><B>图 5. 文档内存大小</B></A><BR><IMG height=535 alt=文档内存大小表 src="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/memsize.gif" width=600></P>
<P>内存大小结果与计时测试不同，因为小的 soap1.xml 文件显示的值表示文件的单个副本而不表示在计时评测中使用的 49 个副本。在大多数模型中，用于简要文档的内存太小以至于无法在图的刻度上显示。</P>
<P>除了 XPP 拉回（直到访问它时才真正构建文档表示）之外，与一些计时测试中显示的差别相比，内存大小测试中模型之间的差别相对较小。延迟的 Xerces 具有最紧凑的表示（当第一次访问表示时将它扩展成基本 Xerces 大小），紧接着是 dom4j。虽然 EXML 废弃包含在其它模型中的空白内容，但是它仍具有最不紧凑的表示。</P>
<P>因为即使最紧凑的模型也要占用大约原始文档文本大小（以字节计）四倍的空间，所以对于大文档来说，所有模型似乎都需要太多的内存。通过提供使用部分文档表示的方法，XPP 拉回和 dom4j 为非常大的文档提供了最好的支持。XPP 拉回通过仅构建实际被访问的表示部分完成该任务，而 dom4j 包含对基于事件处理的支持，使得一次只构建或处理文档的一部分。</P>
<P><A id=13 name=13><SPAN class=atitle3>Java 序列化</SPAN></A><BR>这些测试评测文档表示的 Java 序列化的时间和输出大小。这主要涉及那些使用 Java RMI（“远程方法调用”）在 Java 程序之间传送表示的应用程序（包括 EJB (Enterprise JavaBean) 应用程序）起作用。在这些测试中，仅包含了那些支持 Java 序列化的模型。下列三张图显示了该测试的结果。</P>
<P><A id=figure6 name=figure6><B>图 6. 序列化输出时间</B></A><BR><IMG height=422 alt=序列化输出时间表 src="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/seroutmintime.gif" width=600></P>
<P><A id=figure7 name=figure7><B>图 7. 序列化输入时间</B></A><BR><IMG height=422 alt=序列化输入时间表 src="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/serinmintime.gif" width=600></P>
<P><A id=figure8 name=figure8><B>图 8. 序列化文档大小</B></A><BR><IMG height=442 alt=序列化文档大小表 src="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/sersize.gif" width=600></P>
<P>dom4j 显示了输出（生成序列化的格式）和输入（从序列化的格式重新构建文档）的最好的序列化性能，而 Xerces DOM 显示了最差的性能。EXML 所花费的时间接近 dom4j，但是 EXML 还是具有在表示中使用较少数量对象的优势，因为它废弃空白内容。</P>
<P>如果将文档输出成文本然后进行解析以重新构建文档，而不是使用 Java 序列化，则所有性能 — 时间和大小 — 都会好得多。这里的问题是作为大量唯一的小对象的 XML 文档表示的结构。Java 序列化无法有效处理这种类型的结构，这导致时间和输出大小的开销都很高。</P>
<P><I>可以</I>设计比文本表示小且比文本输入和输出快的文档序列化格式，但是只能通过绕过 Java 序列化来完成。（我有一个项目实现 XML 文档的这种定制的序列化，在我公司的 Web 站点上可以找到其开放源码，请参阅<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/resources">参考资料</A>。）</P>
<P><A id=14 name=14><SPAN class=atitle2>结束语</SPAN></A><BR>不同的 Java XML 文档模型各有所长，但是从性能观点来看，有些模型具有明显的优势。</P>
<P>在大多数方面，XPP 性能处于领先地位。尽管 XPP 是一种新模型，但是对于不需要验证、实体、处理指示信息或注释的中间件类型应用程序来说，它是非常好的选择。它尤其适用于作为浏览器小应用程序或在内存受限的环境下运行的应用程序。</P>
<P>虽然 dom4j 没有与 XPP 同等的速度，但是，它确实提供了具备更标准化的优越性能和功能更全的实现，包括对 SAX2、DOM 甚至 XPath 的内置支持。虽然 Xerces DOM（带有延迟的节点创建）对于小文件和 Java 序列化性能不佳，但是在大多数评测时仍然出色。对于常规 XML 处理，dom4j 和 Xerces DOM 都是很好的选择，对它们的选择取决于您认为是特定于 Java 的特性更重要还是跨语言的兼容性更重要。</P>
<P>JDOM 和 Crimson DOM 在性能测试时一直表现不佳。在小文档情况下还值得考虑使用 Crimson DOM，而 Xerces 表现很差。虽然 JDOM 的开发者已经说明他们期望在正式发行版前专注性能问题，但是从性能观点来看，它确实没有值得推荐之处。然而，如果不进行 API 的重新构建，JDOM 可能难以达到与其它模型匹配的性能。</P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerWorks/cn/i/bg-gold.gif><A id=whitespaceSB name=whitespaceSB><B>使用空白的目的</B></A><BR>XML 规范通常需要保留空白，但是许多 XML 应用程序使用仅为可读性而保留空白的格式。对于这些应用程序，EXML 废弃隔离空白的方法起了作用。 
<P>这些性能中使用的大多数文档属于“为可读性而保留的空白”类别。这些文档被格式化成便于人们查看的形式，一行最多一个元素。结果，无关的空白内容字符串数实际上超过了文档中的元素数量。这大大增加了每一步处理的不必要开销。</P>
<P>支持修剪输入上这种类型空白的选项将有助于提高带有可忽略的空白的应用程序的所有文档模型的性能（除了 EXML 之外）。只要修剪是一个选项，它就不会影响需要完全保留空白的应用程序。解析器级别的支持将更好，因为解析器必须逐一处理输入字符。总之，这种类型的选项将非常有助于许多 XML 应用程序。</P></TD></TR></TBODY></TABLE>
<P>EXML 非常小（以 jar 文件大小为单位）并且在一些性能测试中表现良好。虽然 EXML 具有删除单独空白内容的优势，但是在性能方面不如 XPP。除非您需要 EXML 支持而 XPP 缺少的一种特性，否则在内存受限的环境下，XPP 可能是更好的选择。</P>
<P>虽然 dom4j 性能最好，但是，当前没有一种模型能为 Java 序列化提供良好性能。如果需要在程序之间传递文档，通常的最佳选择是将文档写成文本然后进行解析以重新构建表示。将来，定制序列化格式可能提供一个更好的选择。</P>
<P><A id=next name=next><SPAN class=atitle2>后续内容...</SPAN></A><BR>我已经涵盖了一些文档模型的基本特性，并且显示了几种类型文档操作的性能评测。请记住，虽然，性能只是选择文档模型的一个因素。对于大多数开发者，可用性至少与性能一样重要，并且这些模型使用不同的 API 都可能有喜欢这个而不喜欢那个的理由。</P>
<P>在后续文章中将集中研究可用性，其中我将比较用于完成这些不同模型中相同操作的样本代码。请检查本比较的第二部分。当您等待时，可以通过下面的论坛中的链接提出您对本文的评论和问题与大家共享。</P>
<P><A id=resources name=resources><SPAN class=atitle2>参考资料</SPAN></A></P>
<UL>
<LI>参加关于本文的<A href="javascript:void forumWindow()">论坛</A>。 
<LI>如果您需要背景知识，请尝试 developerWorks的教程 <A href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/xml-onlinecourse-bytitle/E5B3571D78D0821748256A77001189ED?OpenDocument">XML Java 编程</A>、 <A href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/xml-onlinecourse-bytitle/CA45E09F1E2EF41E48256B1B000C6C7B?OpenDocument">理解 SAX </A>和 <A href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/xml-onlinecourse-bytitle/386674F65A47844C48256BD10023D453?OpenDocument">理解 DOM </A>。 
<LI>从<A href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/download.shtml">下载页面</A>下载用于本文的测试程序和文档模型库。 
<LI>在测试程序的<A href="http://www.sosnoski.com/opensrc/xmlbench/index.html">主页</A>上查看更新的测试和测试结果。 
<LI>获取作者关于 <A href="http://www.sosnoski.com/opensrc/xmls/index.html">XML Serial (XMLS) encoding</A> 工作的详细信息作为 Java 序列化的替代项。 
<LI>研究或下载本文中讨论的 Java XML 文档模型： 
<LI style="LIST-STYLE-TYPE: none">
<UL>
<LI><A href="http://xml.apache.org/xerces-j/index.html">Xerces Java</A> 
<LI><A href="http://xml.apache.org/crimson/index.html">Crimson</A> 
<LI><A href="http://jdom.org/index.html">JDOM</A> 
<LI><A href="http://dom4j.org/index.html">dom4j</A> 
<LI><A href="http://www.themindelectric.com/products/xml/xml.html">Electric XML（EXML）</A> 
<LI><A href="http://www.extreme.indiana.edu/soap/xpp/">XML Pull Parser（XPP）</A></LI></UL>
<LI>IBM WebSphere Application Server 包含基于 Xerces Java 的 XML4J 解析器。可在 <A href="http://www-106.ibm.com/developerWorks/cgi-bin/click.cgi?url=http://www-4.ibm.com/software/webservers/appserv/doc/v30/ae/web/doc/begin_here/index.html&amp;origin=x">WAS Advanced edition 3.0 online documentation</A> 中找到关于产品 XML 支持的 how-to 信息。</LI></UL><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A id=author1 name=author1><SPAN class=atitle2>关于作者</SPAN><BR><IMG height=80 alt="作者照片：Dennis M. Sosnoski" src="http://www-900.ibm.com/developerWorks/cn/i/authors/p-sosnoski.jpg" width=64 align=left border=0>Dennis Sosnoski（<A href="mailto:dms@sosnoski.com">dms@sosnoski.com</A>）是西雅图地区 Java 咨询公司 <A href="http://www.sosnoski.com/">Sosnoski Software Solutions, Inc.</A> 的创建者和首席顾问。他具有 30 多年的专业软件开发经验，最近几年，他集中研究服务器方 Java 技术，包括 servlet、Enterprise JavaBeans 和 XML。他已经多次演示了 Java 性能问题和常规服务器端的 Java 技术，他还是 <A href="http://www.sosnoski.com/jxml">Seattle Java-XML SIG</A> 的主席。</A></TD></TR></TBODY></TABLE><BR clear=all><img src ="http://www.blogjava.net/jackybu/aggbug/2356.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-23 13:09 <a href="http://www.blogjava.net/jackybu/articles/2356.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用JDOM处理XML文档</title><link>http://www.blogjava.net/jackybu/articles/2355.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Wed, 23 Mar 2005 05:08:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/2355.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/2355.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/2355.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/2355.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/2355.html</trackback:ping><description><![CDATA[<TABLE width="92%" border=0>
<TBODY>
<TR>
<TD align=middle colSpan=3>
<H2>用JDOM处理XML文档</H2></TD></TR>
<TR>
<TD width="30%">发布日期：2002-04-09</TD>
<TD align=middle width="30%">作者：AYellow</TD>
<TD align=right>阅读人次：4736</TD></TR>
<TR>
<TD colSpan=3 height=15>&nbsp;</TD></TR>
<TR>
<TD align=left colSpan=3>用JDOM处理XML文档<BR><BR>关键词：Java、JDOM、XML、JAXB<BR>欢迎与我讨论（boyofjava@sina.com）<BR><BR>（一）JDOM的介绍以及与JAXB的比较<BR><BR>Java&nbsp;+&nbsp;XML&nbsp;=&nbsp;JDOM&nbsp;！<BR>这就是JDOM设计者的目标。如果你曾经使用过烦人的SAX或是DOM来处理XML，你就会知道为什么要有JDOM或者是JAXB。在今年（2002）的JavaOne会议上JDOM的主要创始人Jason&nbsp;Hunter有一篇精彩的演讲介绍了JDOM技术，题目就是JDOM&nbsp;Makes&nbsp;XML&nbsp;Easy。<BR>在那篇文档里，JDOM被拿来与DOM比较，而我更愿意拿它同JAXB比较。因为JAXB和JDOM都是为了在Java中提供比DOM和SAX更为方便的XML处理接口而开发的，并且通过完全不同的途径来解决这个问题。JDOM的处理方式是与DOM类似的树操作。而JAXB通过DTD和绑定模式来生成访问XML文档的Java代码，将XML映射成了Java对象来操作。你可以根据项目的需要和个人喜好来决定采用哪一个。<BR>JDOM与JAXB的比较，从本身的特点来看：<BR>1)&nbsp;&nbsp;&nbsp;&nbsp;JDOM比JAXB更容易上手。使用JAXB首先要会编写DTD，然后还要会编写绑定模式。JDOM没有这样的要求，如果你会Java和XML，甚至可以说光是看JDOM的javadoc文档就能够使用JDOM。<BR>2)&nbsp;&nbsp;&nbsp;&nbsp;JAXB编写好DTD和绑定模式以后，XML文档被映射成了Java对象，其数据就是Java对象的属性，连数据类型都做好了转换，因此，访问XML文档比JDOM要简便，可以说是一劳永逸。<BR>3)&nbsp;&nbsp;&nbsp;&nbsp;JAXB由某个DTD和绑定模式生成的代码只能访问该DTD所约束的文档。如果想要访问其他XML文档，需要再编写DTD和绑定模式。JDOM可以处理任何XML文档，包括受约束的和不受约束的。<BR><BR>目前JDOM和JAXB都没有正式版本。JDOM的最新版本是beta8，JAXB是1.0&nbsp;early&nbsp;access，其规范版本是0.21。相对而言，JDOM更成熟一些。例如JAXB不支持名字空间、不能向XML文档写入处理指令，有时我们需要保留的换行符和首尾空格在JAXB中自动过滤掉了，就连放在&lt;![CDATA[&nbsp;和&nbsp;]]&gt;里面也不能幸免。JDOM就没有这些限制。如果说以上的3点比较是JDOM和JAXB本身的特点所决定的，几乎不可能改变，那么这里表明，JAXB还需要更多的工作。<BR><BR>（二）获得并安装JDOM<BR>在http://jdom.org可以下载JDOM的最新版本。以JDOM&nbsp;beta8的2进制版本为例。下载后解压缩，JDOM的jar文件就是build目录下的文件jdom.jar，将之加入类路径。另外JDOM还需要lib目录下那些jar文件如xerces.jar的支持。如果在使用中出现以下错误：<BR>java.lang.NoSuchMethodError<BR>或&nbsp;<BR>java.lang.NoClassDefFoundError:&nbsp;org/xml/sax/SAXNotRecognizedException<BR>你需要保证xerces.jar文件在CLASSPATH中位于其他XML类，如JAXP或Crimson之前，这些类文件，包括以前老版本的xerces，可能不支持SAX2.0或DOM&nbsp;Level&nbsp;2。于是导致了上面的错误。<BR><BR>（三）一个简单的例子<BR>JDOM的处理方式有些类似于DOM，但它主要是用SAX实现的，你不必担心处理速度和内存的问题。另外，JDOM中几乎没有接口，的类全部是实实在在的类，没有类工厂类的。其最重要的一个包org.jdom中主要有以下类：<BR>?&nbsp;Attribute<BR>?&nbsp;CDATA<BR>?&nbsp;Comment<BR>?&nbsp;DocType<BR>?&nbsp;Document<BR>?&nbsp;Element<BR>?&nbsp;EntityRef<BR>?&nbsp;Namespace<BR>?&nbsp;ProcessingInstruction<BR>?&nbsp;&nbsp;&nbsp;&nbsp;Text<BR>数据输入要用到XML文档要通过org.jdom.input包，反过来需要org.jdom.output。如前面所说，关是看API文档就能够使用。<BR>我们的例子读入XML文件exampleA.xml，加入一条处理指令，修改第一本书的价格和作者，并添加一条属性，然后写入文件exampleB.xml：<BR>//exampleA.xml<BR>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="GBK"?&gt;<BR>&lt;bookList&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;book&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;Java编程入门&lt;/name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;张三&lt;/author&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;publishDate&gt;2002-6-6&lt;/publishDate&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;price&gt;35.0&lt;/price&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;book&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;XML在Java中的应用&lt;/name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;李四&lt;/author&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;publishDate&gt;2002-9-16&lt;/publishDate&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;price&gt;92.0&lt;/price&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<BR>&lt;/bookList&gt;<BR><BR>//testJDOM.java<BR>import&nbsp;org.jdom.*;<BR>import&nbsp;org.jdom.output.*;<BR>import&nbsp;org.jdom.input.*;<BR>import&nbsp;java.io.*;<BR>public&nbsp;class&nbsp;TestJDOM{<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;args[])throws&nbsp;Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAXBuilder&nbsp;sb&nbsp;=&nbsp;new&nbsp;SAXBuilder();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//从文件构造一个Document，因为XML文件中已经指定了编码，所以这里不必了<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Document&nbsp;doc&nbsp;=&nbsp;sb.build(new&nbsp;FileInputStream("exampleA.xml"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//加入一条处理指令\<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ProcessingInstruction&nbsp;pi&nbsp;=&nbsp;new&nbsp;ProcessingInstruction<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;("xml-stylesheet","href=\"bookList.html.xsl\"&nbsp;type=\"text/xsl\"");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doc.addContent(pi);<BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element&nbsp;root&nbsp;=&nbsp;doc.getRootElement();&nbsp;//得到根元素<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.util.List&nbsp;books&nbsp;=&nbsp;root.getChildren();&nbsp;//得到根元素所有子元素的集合<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element&nbsp;book&nbsp;=&nbsp;(Element)books.get(0);&nbsp;//得到第一个book元素<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//为第一本书添加一条属性<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Attribute&nbsp;a&nbsp;=&nbsp;new&nbsp;Attribute("hot","true");&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;book.setAttribute(a);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element&nbsp;author&nbsp;=&nbsp;book.getChild("author");&nbsp;//得到指定的字元素<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;author.setText("王五\\");&nbsp;//将作者改为王五<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//或&nbsp;Text&nbsp;t&nbsp;=&nbsp;new&nbsp;Text("王五\\");book.addContent(t);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element&nbsp;price&nbsp;=&nbsp;book.getChild("price");&nbsp;//得到指定的字元素<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//修改价格，比较郁闷的是我们必须自己转换数据类型，而这正是JAXB的优势<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;author.setText(Float.toString(50.0f));&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;indent&nbsp;=&nbsp;"&nbsp;&nbsp;&nbsp;&nbsp;";<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolean&nbsp;newLines&nbsp;=&nbsp;true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XMLOutputter&nbsp;outp&nbsp;=&nbsp;new&nbsp;XMLOutputter(indent,newLines,"GBK");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outp.output(doc,&nbsp;new&nbsp;FileOutputStream("exampleB.xml"));<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>};<BR><BR>执行结果exampleB.xml：<BR>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="GBK"?&gt;<BR>&lt;bookList&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;book&nbsp;hot=”true”&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;Java编程入门&lt;/name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;50.0&lt;/author&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;publishDate&gt;2002-6-6&lt;/publishDate&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;price&gt;35.0&lt;/price&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;book&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;XML在Java中的应用&lt;/name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;李四&lt;/author&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;publishDate&gt;2002-9-16&lt;/publishDate&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;price&gt;92.0&lt;/price&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<BR>&lt;/bookList&gt;<BR>&lt;?xml-stylesheet&nbsp;href="bookList.html.xsl"&nbsp;type="text/xsl"?&gt;<BR><BR>在默认情况下，JDOM的Element类的getText()这类的方法不会过滤空白字符，如果你需要过滤，用setTextTrim()&nbsp;。<BR><BR><BR>（四）参考文档<BR>1)&nbsp;&nbsp;&nbsp;&nbsp;JDOM&nbsp;Makes&nbsp;XML&nbsp;Easy&nbsp;(http://www.servlets.com/speaking/jdom-javaone.pdf)<BR>2)&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;Java&nbsp;&amp;#8482;&nbsp;Architecture&nbsp;for&nbsp;XML&nbsp;Binding&nbsp;User’s&nbsp;Guide&nbsp;(http://java.sun.com/xml/jaxb/jaxb-docs.pdf)<BR>3)&nbsp;&nbsp;&nbsp;&nbsp;Web&nbsp;Services&nbsp;Made&nbsp;Easier.&nbsp;The&nbsp;Java&nbsp;TM&nbsp;APIs&nbsp;and&nbsp;Architectures&nbsp;for&nbsp;XML,&nbsp;A&nbsp;Technical&nbsp;White&nbsp;Paper&nbsp;(http://java.sun.com/xml/webservices.pdf&nbsp;)<BR><BR><BR><BR><BR><BR></TD></TR>
<TR>
<TD colSpan=3 height=15>&nbsp;</TD></TR>
<TR>
<TD align=right colSpan=3>整理发布：umbrella</TD></TR>
<TR>
<TD align=right colSpan=3></TD></TR>
<TR>
<TD align=right colSpan=3></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/jackybu/aggbug/2355.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-23 13:08 <a href="http://www.blogjava.net/jackybu/articles/2355.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java XML API 漫谈 </title><link>http://www.blogjava.net/jackybu/articles/1752.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Sat, 05 Mar 2005 14:10:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/1752.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/1752.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/1752.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/1752.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/1752.html</trackback:ping><description><![CDATA[<SPAN class=postbody>在IBM的developerWorks上有几篇非常优秀的关于Java XML API的评测文章，它们是： <BR><BR><A class=postlink href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml" target=_blank>http://www-900.ibm.com/developerWorks/cn/xml/x-injava/index.shtml</A> <BR><BR><A class=postlink href="http://www-900.ibm.com/developerWorks/cn/xml/x-injava2/index.shtml" target=_blank>http://www-900.ibm.com/developerWorks/cn/xml/x-injava2/index.shtml</A> <BR><BR><A class=postlink href="http://www-900.ibm.com/developerWorks/cn/xml/x-databdopt/part2/index.shtml" target=_blank>http://www-900.ibm.com/developerWorks/cn/xml/x-databdopt/part2/index.shtml</A> <BR><BR><A class=postlink href="http://www-900.ibm.com/developerWorks/cn/xml/x-databdopt/part1/index.shtml" target=_blank>http://www-900.ibm.com/developerWorks/cn/xml/x-databdopt/part1/index.shtml</A> <BR><BR>对这几篇文章我想说的就是<SPAN style="FONT-WEIGHT: bold"><SPAN style="COLOR: red">“吐血推荐”</SPAN></SPAN> <BR><BR>Java的XML API这几篇文章该讲的都讲到了，我只想补充几点： <BR><BR>一、Crimson和Xerces恩仇录 <BR><BR>Crimson来自于Sun捐赠给Apache的ProjectX项目，Xerces来自IBM捐赠给Apache的XML4J项目，结果Xerces胜出，成了Apache XML小组全力开发的XML API，而Crimon已经早就不做了，如今Xerces名满天下，到处都是在用Xerces DOM和SAX解析器，只有Sun不服气，非要在JDK1.4里面使用过时的Crimson，让人感觉像是在赌气一样，真是让人可怜又可气！不过IBM发行JDK用的XML 解析器自然是Xerces。 <BR><BR>由于JDK的Class Loader的优先级关系，当你采用JAXP编写XML程序的时候，即使把Xerces包引入CLASSPATH，JDK还是会顽固的使用Crimson，这一点通过打开JVM的verbose参数可以观察到。不过JDK也允许你采用其它的解析器，因此我们可以通过在JRE\lib\目录下建一个jaxp.properties的文件，来替换解析器，jaxp.properties内容如下： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B>引用:</B></SPAN></TD></TR>
<TR>
<TD class=quote>javax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl <BR>javax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl</TD></TR></TBODY></TABLE><SPAN class=postbody><BR>这样就可以使用Xerces，当然你必须还是要把Xerces包放到CLASSPATH下。 <BR><BR>二、JAXP的姗姗来迟 <BR><BR>Sun在XML领域总是后知后觉，等到Sun重视XML的时候，XML的API早就满天 飞了，尤其是IBM具有非常大的领先优势。不过Sun是规范的制订者，于是参考W3C的标准制订了JAXP规范。JAXP不像Xerces和Crimon那样，它只是一个spec，本身是不做任何事情的，它的作用就是提出一个统一的接口，让其它的XML API都来遵循JAXP编程，那么用JAXP写出来的程序，底层的API可以任意切换。 <BR><BR>具体来说JAXP包括了几个工厂类，这就是JDK1.4里面的javax.xml.parsers 包，用来寻找符合DOM标准的XML API实现类的位置；此外JAXP还包括一整套interface，这就是JDK1.4里面的org.w3c.dom那几个包。工厂类负责加载DOM的实现类。那么加载的规则是什么呢？ <BR><BR>我是通过阅读JAXP的源代码知道的，工厂类首先会根据java命令行传入的参数进行寻找，然后在根据JRE\lib\jaxp.properties中定义的实现类寻找，最后什么都找不到的话，就用Crimson。注意Crimons是由Bootstrap Class Loader来load的，如果你不通过上面两个方法来改变工厂的寻找顺序，那么铁定用Crimson了 <IMG alt=Sad src="http://forum.javaeye.com/images/smiles/icon_sad.gif" border=0> <BR><BR>三、 DOM解析器和DOM API <BR><BR>当你严格采用JAXP编程的时候，是遵循W3C的DOm标准的，那么在JAXP底层你实际上可以任意切换不同的DOM实现，例如Xerces，或者Crimon，再或者其它，切换方法就是配置jaxp.properties。因此JAXP就是一些标准接口而已。 <BR><BR>而Xerces和Crimon也不单单是一个DOM实现那么简单，他们本身实际上也包含SAX解析器和DOM解析器。所以一个JAXP程序下面有如下层次： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B>引用:</B></SPAN></TD></TR>
<TR>
<TD class=quote>JAXP应用程序 -&gt; JAXP接口 -&gt; Xerces DOM实现 -&gt; Xerces DOM/SAX 解析器</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>只要你用JAXP编程，那么你就可以切换到Crimson上来 <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B>引用:</B></SPAN></TD></TR>
<TR>
<TD class=quote>JAXP应用程序 -&gt; JAXP接口 -&gt; Crimson DOM实现 -&gt; Crimson DOM/SAX 解析器</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>另外你也可以这样来做： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B>引用:</B></SPAN></TD></TR>
<TR>
<TD class=quote>JAXP应用程序 -&gt; JAXP接口 -&gt; Crimson DOM实现 -&gt; Xerces DOM/SAX 解析器</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>不过如果你的程序不安装JAXP来写，那么就没有办法切换不同的DOM实现了。 <BR><BR>四、不是标准的dom4j和jdom <BR><BR>W3C的DOM标准API难用的让人想撞墙，于是有一帮人开发Java专用的XML API目的是为了便于使用，这就是jdom的由来，开发到一半的时候，另一部分人又分了出来，他们有自己的想法，于是他们就去开发dom4j，形成了今天这样两个API，至于他们之间的性能，功能之比较看看上面我推荐的文章就知道了，jdom全面惨败。 <BR><BR>jdom 相当于上面的 JAXP接口 ＋ Xerces DOM实现部分，它本身没有解析器，它可以使用Xerces或者Crimson的解析器，就是这样： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B>引用:</B></SPAN></TD></TR>
<TR>
<TD class=quote>jdom应用程序 -&gt; jdom API -&gt; Xerces/Crimson解析器</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>dom4j 和jdom类似，不过他自己绑定了一个叫做Alfred2的解析器，功能不是很全，但是速度很快，当没有其它的解析器的时候，dom4j将使用Alfred2解析器，如下： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B>引用:</B></SPAN></TD></TR>
<TR>
<TD class=quote>dom4j应用程序 -&gt; dom4j API -&gt; Xerces/Crimson解析器</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>或者 <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B>引用:</B></SPAN></TD></TR>
<TR>
<TD class=quote>dom4j应用程序 -&gt; dom4j API -&gt; Alfred2解析器</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>你在SF上下载的dom4j.jar是不含 Alfred2解析器的，而dom4j-full.jar包含了 Alfred2解析器，在这种情况下，实际上你什么也不需要，光是一个dom4j-full.jar就全部都包括了。 <BR><BR>因此可以看出采用dom4j/jdom编写的应用程序，已经不具备可移植性了。 <BR><BR>五、小插曲 <BR><BR>Sun是JAXP标准的制订者，甚至很执著的在JDK1.4里面绑定Crimson DOM实现和解析器，然后可笑的是，Sun自己的JAXM RI竟然不是用JAXP写出来的，而是dom4j，制订标准让大家遵守，自己却监守自盗，这未免太说不过去了吧！ <BR><BR>BTW: Hibernate也用的是dom4j来读取XML配置文件，如今已经越来越多的程序纷纷采用dom4j，如果你不是那么在乎可移植性，我强烈建议你采用dom4j。</SPAN><img src ="http://www.blogjava.net/jackybu/aggbug/1752.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-03-05 22:10 <a href="http://www.blogjava.net/jackybu/articles/1752.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>XML在B/S架构开发中的应用 </title><link>http://www.blogjava.net/jackybu/articles/1275.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Thu, 17 Feb 2005 07:41:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/1275.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/1275.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/1275.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/1275.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/1275.html</trackback:ping><description><![CDATA[传统上, 我们利用JSP+Bean来开发用户界面, 这里介绍一种利用XML+XSLT的方法来替代JSP<BR><BR>　　<STRONG>动机:<BR></STRONG><BR>　　1. JSP构造页面太痛苦. 代码嵌入页面导致页面臃肿, 逻辑复杂, 不易编辑.<BR>　　2. 程序员和美工的接口必须精确定义. 复杂的嵌套输出必须构造复杂的显示类作为接口.<BR>　　3. 调试JSP必须等到前端程序结束才能看到效果.<BR>　　4. 调试JSP效率极低, 一点点小的修改都会引起jsp的重新编译. 
<P>　　<B>新的XML的解决方案:</B><BR><BR>　　1. 一个servlet做统一管理. 接受url请求, 映射到对应的程序bean.<BR>　　2. 程序bean只产生XML, 由servlet统一用对应的XSL转换为html.<BR><BR>　　<B>好处:</B><BR><BR>　　1. JDK1.4已经自带XML API和XSLT, 并且支持XSLT 1.0标准. IE6也支持同样标准.<BR>　　2. 开发前先制作接口用的demo XML, 程序员的输出必须符合此格式. 美工根据此格式制作XSL. 接口可视化, 明确定义.<BR>　　3. 只要定义好接口XML, 两边几乎不需要再联合调试. 美工利用接口XML和IE6就可以直接看到效果.<BR>　　4. 程序员只要保证输出的XML的是否符合接口, 直观的做文字性对比.<BR>　　5. 大量页面可重用的部分能方便的抽象出来, 在XSL文件中include/import进来. 高度重用, 标准化.<BR>　　6. 写XSL可大量采用template, 结构清晰, 修改方便, 写页面变成搭积木, 不再有jsp的层层嵌套.<BR>　　7. 对于不同的终端设备, 可以定制不同的XSL, 很方便的就能够支持手机, pda...<BR>　　8. 在程序中利用支持XML的工具, 比如sql2000, 可以直接生成XML结果, 无需复杂编程.<BR><BR>　　<B>坏处:<BR></B><BR>　　1. 因为要进行XSLT转换, 服务器工作量稍微增大.</P>
<P>　　以下是几个关键环节的简单示例:<BR></P>
<TABLE cellSpacing=0 cellPadding=0 width=600 bgColor=#ffffff border=0>
<TBODY>
<TR>
<TD>
<P>==============servlet中的XML转换==========<BR>/**<BR>* XML是String<BR>* XSL从文件中读取<BR>* 直接写入response<BR>*/<BR>//import javax.xml.transform.*;<BR>//import javax.xml.transform.stream.*;</P>
<P>StreamSource xml = new StreamSource(new StringReader(xmlSource));<BR>StreamSource xsl = new StreamSource(xslFileName);</P>
<P>response.setContentType("text/html; charset=UTF-8");<BR>StreamResult result = new StreamResult(response.getWriter());</P>
<P>Transformer trans = TransformerFactory.newInstance().newTransformer(xsl);<BR>trans.setOutputProperty("encoding","UTF-8");<BR>trans.transform(xml, result);</P>
<P>==================接口XML文件样本=============<BR>＜?xml version="1.0" encoding="UTF-8"?＞<BR>＜?xml:stylesheet type="text/xsl" href="xxx.xsl"?＞<BR>＜PAGE＞<BR>＜OUTPUT＞<BR>＜INFO＞推广UNICODE, 解决多语言问题.＜/INFO＞<BR>＜/OUTPUT＞<BR>＜/PAGE＞</P>
<P>==================XSL文件样本=============<BR>＜?xml version="1.0" encoding="UTF-8"?＞<BR>＜?xml:namespace prefix = xsl /＞＜xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"＞<BR>＜xsl:template match="/"＞<BR><BR>＜P align=center＞<BR>＜xsl:value-of select="page/output/info"＞＜/xsl:value-of＞<BR></P>＜/xsl:template＞<BR>＜/xsl:stylesheet＞ 
<P></P></TD></TR></TBODY></TABLE>
<P>　　<B>考虑:<BR></B><BR>　　1. 可以把XSLT转换放在IE进行, IE6以下版本要安装MSXML3, 对客户端限制太多.<BR>　　2. 在客户端的XSLT转换必须完全读入XML+XSL, 在网速不高或者内容很多的时候反应相当迟钝.<BR>　　3. 手机, pda等设备不能进行XSLT转换, 所以XSLT转换还是放在服务器上比较合适.<BR>　　4. 因为一般提交内容都比较简单, 所以没有采用XML处理, 在程序中也尽量少用XML, 避免影响速度.<BR>　　5. 可以在servlet判断特定参数, 比如发现有xml-view参数时, 不做XSLT转换, 直接输出XML到浏览器, 供程序调试用.</P>
<P>　　<B>说明:</B><BR><BR>　　1. 这里只是利用XSLT的功能来解决网站建设中最头痛的界面和逻辑分开问题, 不是基于XML的内容处理.<BR>　　2. 如果你想赶时髦, 用XML来全副武装, 可以参考cocoon2, http://xml.apache.org/cocoon/. 但是请记住, cocoon </P>
<P>　　还不完善, 你将碰到乱码, 速度, 文档不足的问题, 但是它的思想的确值得学习.<BR>　　3. 在tomcat, resin中有用filter实现的xslt转换，可参考。</P><img src ="http://www.blogjava.net/jackybu/aggbug/1275.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-02-17 15:41 <a href="http://www.blogjava.net/jackybu/articles/1275.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java与XML（一）基础 </title><link>http://www.blogjava.net/jackybu/articles/1208.html</link><dc:creator>辰</dc:creator><author>辰</author><pubDate>Tue, 15 Feb 2005 03:28:00 GMT</pubDate><guid>http://www.blogjava.net/jackybu/articles/1208.html</guid><wfw:comment>http://www.blogjava.net/jackybu/comments/1208.html</wfw:comment><comments>http://www.blogjava.net/jackybu/articles/1208.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jackybu/comments/commentRss/1208.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jackybu/services/trackbacks/1208.html</trackback:ping><description><![CDATA[<P>JAXP API--嵌入不同的解释器<BR><BR>SAX解释器+DOM解释器+XSL转换器</P>
<P>javax.xml.parsers中加载XML文档的类：<BR>DocumentBuilder<BR>DocumentBuildrFactory<BR>SAXParser<BR>SAXParserFactory<BR>=====================================<BR>SAX API</P>
<P>SAX的XML解释器：Apache的Xerces或Crimson</P>
<P>处理XML文档的接口：<BR>ContentHandler<BR>EntityResolver<BR>ErroHandler<BR>DTDHandler</P>
<P>DeclHandler<BR>LexicalHandler<BR>======================================<BR>DOM API</P>
<P>两个DOM标准：DOM Level1 DOM Level 2 Core</P>
<P>节点<BR>Node-节点类型接口层次结构的根。<BR>Document-树结构的根<BR>Element-XML元素<BR>Text-元素内的文本<BR>Attr-元素的特性<BR>CDATA Sectionn-CDATA<BR>NodeList-子节点的集合<BR>ProcessingInstruction-指令<BR>Comment-包含注释的信息<BR>DocumentFragment-Document的消减版，用于在树中移动节点<BR>DocumentType-文档类型定义的子集。<BR>Entity-DTD中的实体标记<BR>EntityReference-XML文档中的实体引用<BR>Notation-DTD中的符号标记</P>
<P><BR>从程序中读取X M L文档基本上有三种方式：<BR>1把X M L只当做一个文件读取，然后自己挑选出其中的标签。这是黑客们的方法，我们不推荐这种方式。<BR>你很快会发现处理所有的特殊情况（包括不同的字符编码，例外约定，内部和外部实体，缺省属性等）比想象的困难得多；<BR>你可能不能够正确地处理所有的特殊情况，这样你的程序会接收到一个非常规范的X M L文档，却不能正确地处理它。<BR>要避免这种想法：XML解析器似乎并不昂贵（大多数是免费的）。<BR>2可以用解析器分析文档并在内存里创建对文档内容树状的表达方式：解析器将输出传递给文档对象模型，即DOM。<BR>这样程序可以从树的顶部开始遍历，按照从一个树单元到另一个单元的引用，从而找到需要的信息。<BR>3也可以用解析器读取文档，当解析器发现标签时告知程序它发现的标签。<BR>例如它会告知它何时发现了一个开始标签，何时发现了一些特征数据，以及何时发现了一个结束标签。<BR>这叫做事件驱动接口，因为解析器告知应用程序它遇到的有含义的事件。<BR>如果这正是你需要的那种接口，可以使用SAX。</P>
<P>SAX是只读的<BR>DOM可以从XML原文件中读取文档，也可以创建和修改内存中的文档。相比较而言，SAX是用来读取XML文档而不是书写文档。</P>
<P>可扩展样式语言(XSL,eXtensible Sytlesheet Language）是一种基于XML的语言，<BR>它被设计用来转换XML文档到另一种XML文档或转换XML文档为可翻译对象。<BR>原始的XSL语言已经被分割成三种不同的语言：<BR>1转换工具（XSLT）是一种转换XML文档到其他XML文档的语言<BR>2翻译工具（XSLF—可以包括X S LT的使用）<BR>3XML分级命令处理工具（XPath）<BR>XSL有它自已的根，不管是在层叠样式表（CSS）中还是在一种叫DSSSL(文档样式语义和规格语言—读为'deessel')的语言中。<BR>随着它的发展，XSL的样式表现变得更接近于CSS和远离DSSSL</P>
<P>================================================================================</P>
<P>Java与XML（二）用java编写xml的读写程序</P>
<P>这是读取xml文件的java程序，我调试好的。采用的是dom方式读取xml文件到Vector中。<BR>package src;<BR>import java.io.*;<BR>import java.util.Vector;<BR>import javax.xml.parsers.*;<BR>import org.w3c.dom.*;<BR>public class readxml {<BR>&nbsp;static Document document;<BR>&nbsp;private boolean validating;<BR>&nbsp;public readxml() {<BR>&nbsp;}<BR>&nbsp;public Vector toRead(String filename) {<BR>&nbsp;&nbsp;Vector title=new Vector();<BR>&nbsp;&nbsp;Vector content=new Vector();<BR>&nbsp;&nbsp;String myStr=new String();<BR>&nbsp;&nbsp;try {<BR>&nbsp;&nbsp;&nbsp;DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();<BR>&nbsp;&nbsp;&nbsp;factory.setValidating(validating);<BR>&nbsp;&nbsp;&nbsp;DocumentBuilder builder = factory.newDocumentBuilder();<BR>&nbsp;&nbsp;&nbsp;document = builder.parse(new File(filename));<BR>&nbsp;&nbsp;&nbsp;document.getDocumentElement().normalize();<BR>&nbsp;&nbsp;&nbsp;Node node = document.getFirstChild();<BR>&nbsp;&nbsp;&nbsp;NodeList list = node.getChildNodes();<BR>&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; list.getLength(); i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;Node nodeitm = list.item(i);<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (nodeitm.getNodeName().equals("Title")) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;myStr=nodeitm.getFirstChild().getNodeValue();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;title.addElement(myStr);//getFirstChild()<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (nodeitm.getNodeName().equals("Content")) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;myStr=nodeitm.getFirstChild().getNodeValue();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content.addElement(myStr);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;} catch (Exception exp) {<BR>&nbsp;&nbsp;&nbsp;exp.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;return null;<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;Vector all=new Vector();<BR>&nbsp;&nbsp;all.add(title);<BR>&nbsp;&nbsp;all.add(content);&nbsp;&nbsp;<BR>&nbsp;&nbsp;return all;<BR>&nbsp;}</P>
<P>&nbsp;public static void main(String[] args) {<BR>&nbsp;&nbsp;Vector A;<BR>&nbsp;&nbsp;readxml my = new readxml();<BR>&nbsp;&nbsp;A = my.toRead("f:\\tomcat5\\webapps\\myxml\\xmldata\\9.xml");<BR>&nbsp;&nbsp;for (int i = 0; i &lt; A.size(); i++) {<BR>&nbsp;&nbsp;&nbsp;System.out.println(A.elementAt(i));<BR>&nbsp;&nbsp;}<BR>&nbsp;}<BR>}<BR>这是将xml写入文件。其中，transformer.setOutputProperty(OutputKeys.ENCODING,"GB2312")关系到编码问题，非常重要。<BR>import org.w3c.dom.*;<BR>import javax.xml.parsers.*;<BR>import javax.xml.transform.*;<BR>import javax.xml.transform.dom.DOMSource;<BR>import javax.xml.transform.stream.StreamResult;<BR>import java.io.*;<BR>public class writexml {<BR>&nbsp;private Document document;<BR>&nbsp;private String filename;<BR>&nbsp;<BR>&nbsp;public writexml(String name) throws ParserConfigurationException{<BR>&nbsp;&nbsp;filename=name;<BR>&nbsp;&nbsp;DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();<BR>&nbsp;&nbsp;DocumentBuilder builder=factory.newDocumentBuilder();<BR>&nbsp;&nbsp;document=builder.newDocument();<BR>&nbsp;}<BR>&nbsp;public void toWrite(String mytitle,String mycontent){<BR>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;Element root=document.createElement("WorkShop");<BR>&nbsp;&nbsp;document.appendChild(root);<BR>&nbsp;&nbsp;Element title=document.createElement("Title");<BR>&nbsp;&nbsp;title.appendChild(document.createTextNode(mytitle));<BR>&nbsp;&nbsp;root.appendChild(title);<BR>&nbsp;&nbsp;Element content=document.createElement("Content");<BR>&nbsp;&nbsp;content.appendChild(document.createTextNode(mycontent));<BR>&nbsp;&nbsp;root.appendChild(content);<BR>&nbsp;&nbsp;}<BR>&nbsp;public void toSave(){<BR>&nbsp;&nbsp;try{<BR>&nbsp;&nbsp;&nbsp;TransformerFactory tf=TransformerFactory.newInstance();<BR>&nbsp;&nbsp;&nbsp;Transformer transformer=tf.newTransformer();<BR>&nbsp;&nbsp;&nbsp;DOMSource source=new DOMSource(document);<BR>&nbsp;&nbsp;&nbsp;transformer.setOutputProperty(OutputKeys.ENCODING,"GB2312");<BR>&nbsp;&nbsp;&nbsp;transformer.setOutputProperty(OutputKeys.INDENT,"yes");<BR>&nbsp;&nbsp;&nbsp;PrintWriter pw=new PrintWriter(new FileOutputStream(filename));<BR>&nbsp;&nbsp;&nbsp;StreamResult result=new StreamResult(pw);<BR>&nbsp;&nbsp;&nbsp;transformer.transform(source,result);<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;catch(TransformerException mye){<BR>&nbsp;&nbsp;&nbsp;mye.printStackTrace();<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;catch(IOException exp){<BR>&nbsp;&nbsp;&nbsp;exp.printStackTrace();<BR>&nbsp;&nbsp;}<BR>&nbsp;}<BR>&nbsp;public static void main(String args[]){<BR>&nbsp;&nbsp;try{<BR>&nbsp;&nbsp;writexml myxml=new writexml("f:\\tomcat5\\webapps\\myxml\\xmldata\\9.xml");<BR>&nbsp;&nbsp;myxml.toWrite("中文题目","中文内容");<BR>&nbsp;&nbsp;myxml.toSave();<BR>&nbsp;&nbsp;System.out.print("Your writing is successful.");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;catch(ParserConfigurationException exp){<BR>&nbsp;&nbsp;&nbsp;exp.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;System.out.print("Your writing is failed.");<BR>&nbsp;&nbsp;}&nbsp;&nbsp;<BR>&nbsp;}<BR>}</P>
<P>=================================================================================</P>
<P>利用（二）中我写的两个函数（放在package src中），这次实现web页面的操作。<BR><STRONG>index.html:<BR></STRONG>&lt;%@ page language="java" pageEncoding="GB2312"%&gt;<BR>&lt;body&gt;<BR>&lt;p&gt;&amp;nbsp;&lt;/p&gt;<BR>&lt;p&gt;&amp;nbsp;&lt;/p&gt;<BR>&lt;p&gt;&amp;nbsp;&lt;/p&gt;<BR>&lt;table width="60%" border="1" align="center"&gt;<BR>&nbsp; &lt;tr&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;td&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt;&lt;font size="4" color="#003399"&gt;管理测试程序&lt;/font&gt;&lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;form name="readform" method=post action="load.jsp"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt;&lt;font color="#FF0000"&gt;读取xml文件： &lt;/font&gt;&lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt; &lt;font color="#FF0000"&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;input type="text" name="mypath" size="50" value="c:\\eclipse\\workspace\\myxml\\xmldata\\1.xml"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/font&gt;&lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a href="#" onClick=submit()&gt;读取&lt;/a&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt;&amp;nbsp;&lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/form&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;form name="writeform" method=post action="create.jsp"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt;&lt;font color="#FF0000"&gt;写入xml文件：&lt;/font&gt; &lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt;请填写路径:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;input type="text" name="mypath" width="100" size="50" <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value="c:\\eclipse\\workspace\\myxml\\xmldata\\11.xml"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt;请填写题目: <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;input type="text" name="mytitle" width="100" size="50"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt;请填写内容: <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;textarea name="mycontent" cols="100"&gt;&lt;/textarea&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p align="left"&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a href="#" onClick=submit()&gt;写入&lt;/a&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/p&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/form&gt;<BR>&lt;/td&gt;<BR>&nbsp; &lt;/tr&gt;<BR>&lt;/table&gt;<BR>&lt;/body&gt;</P>
<P><STRONG>写入的页面create.jsp</STRONG>：<BR>&lt;%@ page language="java" pageEncoding="GB2312"%&gt;<BR>&lt;%@ page import="src.*" %&gt;<BR>&lt;%@ page import="org.w3c.dom.*"%&gt;<BR>&lt;%@ page import="javax.xml.parsers.*"%&gt;<BR>&lt;%@ page import="javax.xml.transform.*"%&gt;<BR>&lt;%@ page import="javax.xml.transform.dom.DOMSource"%&gt;<BR>&lt;%@ page import="javax.xml.transform.stream.StreamResult"%&gt;<BR>&lt;%@ page import="java.io.*"%&gt;<BR>&lt;html&gt;<BR>&lt;body&gt;<BR>&lt;%<BR>String mypath=(String)request.getParameter("mypath"); <BR>String mytitle=(String)request.getParameter("mytitle"); <BR>String mycontent=(String)request.getParameter("mycontent"); </P>
<P>mypath=new String(mypath.getBytes("ISO-8859-1"),"GB2312");<BR>mytitle=new String(mytitle.getBytes("ISO-8859-1"),"GB2312");<BR>mycontent=new String(mycontent.getBytes("ISO-8859-1"),"GB2312");<BR>try{<BR>writexml myxml=new writexml(mypath);<BR>myxml.toWrite(mytitle,mycontent);<BR>myxml.toSave();<BR>out.print("Your writing is successful.");<BR>}<BR>catch(ParserConfigurationException exp){<BR>&nbsp;exp.printStackTrace();<BR>&nbsp;out.print("Your writing is failed.");<BR>}<BR>%&gt;<BR>&lt;/body&gt;<BR>&lt;/html&gt;</P>
<P><BR><STRONG>读取xml的页面load.jsp：</STRONG><BR>&lt;%@ page language="java" pageEncoding="GB2312"%&gt;<BR>&lt;%@ page import="src.*" %&gt;<BR>&lt;%@ page import="java.io.*" %&gt;<BR>&lt;%@ page import="java.util.Vector" %&gt;<BR>&lt;%@ page import="javax.xml.parsers.*" %&gt;<BR>&lt;%@ page import="org.w3c.dom.*" %&gt;<BR>&lt;html&gt;<BR>&lt;body&gt;<BR>&lt;%<BR>Vector A=new Vector();<BR>String mypath=(String)request.getParameter("mypath");&nbsp; <BR>out.println(mypath);%&gt;<BR>&lt;p&gt;<BR>&lt;%<BR>readxml my = new readxml();<BR>A = my.toRead(mypath);<BR>for (int i = 0; i &lt; A.size(); i++) {<BR>out.println(A.elementAt(i));<BR>%&gt;<BR>&lt;p&gt;<BR>&lt;%<BR>}<BR>%&gt;<BR>&lt;/body&gt;<BR>&lt;/html&gt;<BR>这个写程序还有一个缺陷，它只是创建xml格式和内容，而不是改写已有文件。<BR>如果您写出了改写文件的程序望能<A href="mailto:delva@163.com">交流</A>。</P></SPAN><img src ="http://www.blogjava.net/jackybu/aggbug/1208.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jackybu/" target="_blank">辰</a> 2005-02-15 11:28 <a href="http://www.blogjava.net/jackybu/articles/1208.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>