级别: 中级 Berthold Daum (berthold.daum@bdaum.de), 总裁, BDaum Industrial Communications
2004 年 1 月 01 日 Streaming API for XML (StAX) 不仅能解析 XML 文档,而且可以把 XML 文档写入一个输出流。这篇技巧展示了客户应用程序如何使用底层的、基于指针的 StAX API 有效地创建 XML 文档。
直到最近,程序员通过编程创建 XML 文档还只有两种选择。一种是把序列化的 XML 内容写入输出流,第二种是使用 DOM。
这两种方法都有严重的缺陷。对于第一种情况,程序员完全负责保证结果文档是格式正规的。程序员必须处理其中的细节,比如起始标签和结束标签的匹配,以及字符内容中特殊字符的转义,如小于号(<)和逻辑与(&)。这可能造成程序的实现冗长乏味而且容易出错。另一方面,尽管 DOM 把程序员从这种沉重的负担中解放出来,但是引入了巨大的开销:在序列化到输出流之前,整个文档必须首先在内存中构造一棵节点树。
进入 StAX
Streaming API for XML (StAX) 彻底改变这一切。与 Simple API for XML (SAX) 不同,StAX 提供了编写 XML 文档的 API。更具体地说,它提供了两种 API:底层的、基于指针的 API(
XMLStreamWriter )和高级的、基于事件的 API(
XMLEventWriter )。基于指针的 API 最适合用于数据绑定的情况(比如从应用程序数据创建文档),而基于事件的 API 则通常用于管道式的情况,从输入文档中的数据构造新的文档。
下面的例子是使用基于指针的 API 实现的。(我将在下篇技巧中讨论基于事件的 API。)基于指针的 API 为创建 XML 信息集中的不同元素提供了各种专门的方法,比如元素、属性、处理指令、数据类型声明和字符内容。这些方法解决了许多格式化问题。比如,
writeCharacters() 方法自动转义像小于号(<)、大于号(>)和逻辑与(&)这样的特殊字符。而
writeEndDocument() 则自动关闭所有打开的结构。因此即使把本例中最后一次对
writeEndElement() 的调用注释掉也没有关系。
StAX 甚至可以为没有正式声明的名称空间生成名称空间前缀。但是只有在输出工厂的
javax.xml.stream.isPrefixDefaulting 属性设为
true 时才会这么做。如果该属性被设为
false ,您就必须明确地声明每个名称空间前缀,并对每个名称空间使用
setPrefix() 和
writeNamespace() 方法。清单 1 中,我已经把这些方法调用注释掉了,因为我已经把前缀默认设置为
true 。
清单 1. 编写文档
import javax.xml.stream.*;
public class XMLWriter {
// Namespaces
private static final String GARDENING = "http://com.bdaum.gardening";
private static final String XHTML = "http://www.w3.org/1999/xhtml";
public static void main(String[] args) throws XMLStreamException {
// Create an output factory
XMLOutputFactory xmlof = XMLOutputFactory.newInstance();
// Set namespace prefix defaulting for all created writers
xmlof.setProperty("javax.xml.stream.isPrefixDefaulting",Boolean.TRUE);
// Create an XML stream writer
XMLStreamWriter xmlw =
xmlof.createXMLStreamWriter(System.out);
// Write XML prologue
xmlw.writeStartDocument();
// Write a processing instruction
xmlw.writeProcessingInstruction(
"xml-stylesheet href='catalog.xsl' type='text/xsl'");
// Now start with root element
xmlw.writeStartElement("product");
// Set the namespace definitions to the root element
// Declare the default namespace in the root element
xmlw.writeDefaultNamespace(GARDENING);
// Writing a few attributes
xmlw.writeAttribute("productNumber","3923-1");
xmlw.writeAttribute("name","Nightshadow");
// Declare XHTML prefix
// xmlw.setPrefix("xhtml",XHTML);
// Different namespace for description element
xmlw.writeStartElement(XHTML,"description");
// Declare XHTML namespace in the scope of the description element
// xmlw.writeNamespace("xhtml",XHTML);
xmlw.writeCharacters(
"A tulip of almost black color. \nBlossoms in April & May");
xmlw.writeEndElement();
// Shorthand for empty elements
xmlw.writeEmptyElement("supplier");
xmlw.writeAttribute("name","Floral22");
// xmlw.writeEndElement();
// Write document end. This closes all open structures
xmlw.writeEndDocument();
// Close the writer to flush the output
xmlw.close();
}
}
|
注意,StAX 不能保证文档的格式正规性。仍然可能生成违反 XML 推荐标准的文档,比如一个文档中有多个根元素或者多个 XML 序言,标签名和属性名中包含空格或 XML 不支持的字符。StAX 实现可以检查这些问题,但并不要求这么做(参考实现中就没有这样做)。尽管如此,StAX
XMLStreamWriter 对于输出原始 XML 数据仍然是一个很大的改进,而且开销只是 DOM 的一个零头。
结束语
这篇技巧说明了如何使用 StAX 基于指针的 API 有效地编写 XML 文档。在下一篇文章中,我将介绍如何使用基于事件的 API 合并两个 XML 文档。
参考资料
关于作者  | |  | Berthold Daum 是德国 Ltzelbach 的一名顾问和作家。他的近作有
System Architecture with XML和
Modeling Business Objects with XML Schema(均由 Morgan Kauffman 出版),要了解这些书的更多信息,请访问
http://www.bdaum.de。可以通过
berthold.daum@bdaum.de与 Berthold 联系。
|
对本文的评价
|