XML与Java技术之XML持久性的三种方式
必须将 XML 存储在某个地方
XML 是一种伟大的数据格式 — 显 而易见,整个 IBM developerWorks 专区都在专注于研究这个主题。2007 年, 关于 XML 的讨论多半是 Web 服务,或者 XML 和 Java™ 对象之间的转换 ,或者读取 XML 配置文件,或者甚至是使用 XML 格式的数据库代替关系或面向 对象的数据库。
目前,我们还没有听到有人在谈论如何将所使用的内存表示 — DOM、 JDOM 等等 — 中的 XML 保存到静态文件中去,并在其中填满尖括号和引 号。坦白的说,获取 XML 并将其写入文件算不上激动人心 — 不过这却是 有必要的。试问,编程领域若永远都不能将 XML 持久化存储到文件中会是怎样 一番情境?您可以在内存中创建 XML 文档,并且甚至可以将其发送给应用程序 中的其他组件(或者其他应用程序的组件);但是却无法存储这些 XML。您可以 使用 XML 存储配置数据,同时编写各种工具来读取该数据,但是实际上却无法 存储配置文件本身。您甚至还可以读取 SOAP 信封的内容 — 但是却无法 将这些内容存储在磁盘上,以供应用程序离线时使用。
显然,将 XML 写入文件非常重要。事实上,如果只是想将数据停留在内存中 并且不需要担心数据的存储方式,您可以想象得到不需要存储 XML 的编程世界 是什么样子,毫无疑问这在如今的编程领域中是不可能的。
因此,问题 十分简单:如何将 XML 持久化存储到文件中去?我假定本文的读者需要自己处 理这一任务。换句话说,如果您在编程中从未涉及到持久化存储 XML,那么本文 将使您受益匪浅。(但是,了解如何执行这些任务会更利于对文章的理解)对于 那些确实关注持久性存储的人,我总结了三种相当常用的主流方法:
使用 DOM 和 JDOM 之类的 API 将 XML 数据结构直接写入文件
使用 Transformation API for XML (TrAX) 和标识转换(identity transformation)持久化存储您的 XML
使用 JAXB 之类的较高级别的 API 处理持久化存储
直接使用 API
如果使用一个或多个 API 读取 XML,那么很明显的一个方法就是使用与之相 同的 API 将 XML 写入文件。比如说,如果您使用 JDOM API 和一个 JDOM Document 对象操作 XML,那么可以编写以下代码:
XMLOutputter outputter = new XMLOutputter();
outputter.setFormat(Format.getPrettyFormat());
outputter.output(myDocument, new FileWriter ("outputFile.xml"));
与此类似,在 DOM Level 3 中可以使用新的 Load 和 Save API:
DOMWriter writer = new org.apache.xml.serialize.XMLSerializer();
writer.setNewLine("\r\n");
writer.setEncoding("UTF-8");
writer.writeNode(new FileOutputStream(new File("outputFile.xml")), myDocument);
注意,使用新 DOM API 的方法多种多样,其中有一些具有较低的供应商独立 性。上面的示例代码中含有一个特定于 Xerces 的类,但是其他方法不会像它一 样与某个特定的供应商类紧密绑定在一起。从学习的角度来说,那些方法都不够 直观,因此我保留了特定于供应商的代码。
优点
这种方法的优势是可以相当直接地与您的 API 进行交互,从而实现良好和全 面的控制。您可以设置新行,您可以处理首行缩进,您可以控制输出文件的各个 方面。此外,还可以尽可能拉近您与文件之间的距离;即没有包装器 API 也没 有间接层,您可以直接编写 XML。如果您对 JDOM 和 DOM 比较熟悉,那么它们 将是输出 XML 的不二选择。
缺点
任何方法有优点就必然有缺点。虽然您可以全面地控制输出的各种细节,但 是如果输出配置不当则会导致诸多混乱的问题。换行错误,编写错误和 I/O 错 误都是这种方法产生的一些常用问题。除此之外,您还工作于一个非常低的层次 ,并没有大量的辅助工具(JDOM 在 Format.getPrettyFormat() 和 Format.getCompactFormat() 方法中提供了一些:而 DOM 几乎是一毛不拔)。 这意味着您必须要理解编码、输出格式、缩进格式,以及对输出有影响的任何内 容。
转换格式
另一个流行的选择是使用 TrAX 和标识转换。TrAX 是 Transformation API for XML 的缩写,它现在是 JAXP 的一部分,而 Java 平台的每一个发行版中都 含有 JAXP(除了 Micro Edition)。TrAX 允许您使用 XSL 样式表对 XML 进行 转换。由于 XML 经常需要结合 SAX 和 DOM 一起使用,因此 TrAX 可以接收 SAX 事件和 DOM Document 作为输入,并能够轻易地产生输出文件。此外,TrAX 还可以轻松地对这些格式进行相互转换。比如说,您可以使用以 DOM 表示的 XML 文档作为输入,并对它进行转换,然后再将输出发送到文件中。或者您也可 以读取文件中的内容,并对它进行转换操作,然后再将结果文档存储到 DOM Document 中。
这种方法的另外一个作用是,您可以使用一个不含任何文档操作的样式表, 并使用某种格式作为输入,然后将这种格式输出为任意其他的格式。使用不具转 换功能的样式表 — 实际上指不执行任何操作但回转所接收的输入内容的样式表 — 称作 标识转换(identity transformation)。因此您可以从文件中获得文 档,然后应用标识转换,最终在 DOM Document 中生成相同的 XML。如果您采用 相反的方式 — 从 DOM 到文件 — 那么实际上可以实现持久化存储 XML。这种 方法类似于以下过程:
Source domSource = new DOMSource(myDOMDocument);
Result fileResult = new StreamResult(new File ("outputFile.xml"));
TransformerFactory factory = TransformerFactory.newInstance ();
Transformer transformer = factory.newTransformer();
transformer.transform(domSource, fileResult);
此处,DOM 文档中的 XML 最终转换为了一个 outputFile.xml 文件。
TrAX 的优点
TrAX 最大的优点就是易于使用。拥有 Java 平台访问权的任何人都可以使用 它,并且不需要对 SAX 或 DOM 有深入的了解。因此,这对于只有基本 XML 编 程技能的开发人员来说是一个极具吸引力的选择。此外,不熟悉 SAX 或 DOM 的 初级程序员也可以使用 TrAX — 只需要了解 10 到 20 行函数代码 — 快速将 XML 持久化存储到文件中,或者甚至是 DOM Documents 和 SAX 事件中。
TrAX 的缺点
使用 TrAX 的最大缺点是:虽然可以很容易执行标识转换,但是处理输出细 节却需要很高的技巧。换行、编码、空格和缩进 — 所有这些都是 TrAX 提供的 配置选项,但是它并不像使用 DOM 或 JDOM 直接配置那样简单。在大多数情况 下,TrAX 为普通任务所提供的易用性常常伴随着较低的灵活性,至少不能开箱 即用。
注意:在输出方面,使用 TrAX 和标记转换几乎可以实现 JDOM 或 DOM 可以 完成的所有任务;只是不够简单或直观而已。您只需要了解一些 XSLT 和 TrAX API 的知识,这两者与所执行的实际输出任务并没有密切的联系。
使用数据绑定实现持久化存储
将 XML 转换为静态格式的另一种方法 — 特别是您希望这种格式是位于磁盘 上的文件 — 是使用 JAXB 之类的数据绑定 API。虽然通常人们不会考虑使用数 据绑定来实现持久化存储,但是它可以有效地实现:读取内存中表示的 XML 文 档并将其写入文件。
我没有太多时间详细介绍数据绑定的概念(您可以在 developerWorks 网站 上阅读一些这方面的文章);下面这段简短的代码使用了 JAXB 方式的数据绑定 实现持久性存储:
FileOutputStream stream = new FileOutputStream ("outputFile.xml");
Marshaller marshaller = myJaxbContext.createMarshaller();
marshaller.marshal(myJavaObject, stream);
您可以设置一些选项,比如说输出文件的编写,所有设置都在 Marshaller 对象中。事实上,JAXB 在设置输出属性方面的灵活性与前面两种方法是不相上 下的。
JAXB 的优点
JAXB 的最大优点就是:它具有极大的易用性,特别是对于一些简单的任务。 同时,虽然人们仍然认为 SAX 和 DOM 是主流方法(至少在普通 Java 编程领域 是如此),但是 JAXB 对于使用 Java 语言的任何人来说都是家常便饭。这意味 着我们可以找到更多 JAXB 方面的文章和教程(2007 发布的一篇调查文章将证 实这一点)。此外,对 JAXB 的支持也要优于 DOM 和 SAX。SAX 和 DOM 是 Java 平台标准版本的一部分,而 JAXB 在很大程度上是由 Sun Microsystems, Inc. 发明的。因此,JAXB 的支持稍高一筹也不足为奇了。
此外,使用 JAXB 基本不需要掌握任何 XML 知识。您可以操作普通 Java 对 象 — 不是特定于 XML 的对象,如 DOM 的 Node 或 Text 接口 — 并将这些对 象直接表示为 XML。这意味着较低的入门门槛,并且任何人都希望可以在很短的 时间内掌握它,尤其是当老板在办公室对着您颐指气使的时候。
JAXB 的缺点
有其利必有其弊。JAXB 的不足之外在于我们不需要过多了解 XML 便可以使 用它。这看上去似乎是我刚刚提到的优点,但是它同时也潜藏着缺点。对 XML 的了解越少,要合理使用 JAXB 就愈加显得困难。您可以轻松地生成一个 XML 文件,但是却会发现这个文件的格式并不可用,或者它只含有一部分需要持久化 存储的对象,或者其中的对象与您所编制的对象并不相同。
所有这些常常会导致开发人员将 JAXB 放在一旁,或者大量学习 XML、SAX 和 DOM 方面的知识。这样一来,许多开发人员都会继续使用 SAX 和 DOM 实现 持久性存储,而使用 JAXB 只是为实现其最简单的功能:在 XML 和 Java 对象 之间相互转换。
另一种选择……
我特意将最后一种选择留给大家思考:将 XML 作为一系列比特、节点和字符 串直接写入 FileOutputStream 或 FileWriter。毫无疑问,这种方法可以将 XML 写入文件,并且这种方法的采用也相当多;但是在本例中,持久化存储已有 XML 数据并没有从非 XML 格式的数据中创建 XML 那么频繁。您可以认出这种代 码,它们通常类似于以下形式:
String xmlString = setupXMLBuffer(
new StringBuffer("<firstName>")
.append(customer.firstName)
.append("</firstName>")
.append("<lastName>")
.append(customer.lastName)
.append("</lastName>")
// etc...
.toString()
);
bufferedWriter.write(xmlString);
// other file I/O code
这段代码并没有任何错误;我们只是将数据持久化存储于 XML 中,所有操作 都在一步之内完成。因此,关于如何持久化存储数据,以及哪种方式是最佳的, 这些问题都无关紧要。写入数据并将其存入 XML 的操作是无法分开的,因此再 多的讨论也无济于事。
结束语
我们应该如何处理 XML 持久性存储呢?并没有完全正确的答案。也就是说, Java 和 XML 开发人员都需要经过仔细讨论才能做出选择。您趋向于使用一致的 方法解决通用问题吗?有没有一种持久性存储方法可以在磁盘上生成易于读取、 使用并能发送给其他应用程序的 XML 文档呢?
对于大多数技巧,我的观点是人们应该重点关注这些技巧是否能够真正奏效 。如果您发现某个技巧更适用于其他人,那么您应该能够提高自己的编程技巧 — 至少,这是基本思想!因此,请花几分钟逛逛 developerWorks 网站的 Java 和 XML 论坛,并让我们知道您所使用的持久性存储方法。如果需要基于一些特 定的功能,也请告诉我们。我希望能在论坛中见到您的身影。
www.zzzyk.com电脑知识网,平板电脑知识也很精彩