充分利用 Xerces-C++,第 2 部分

这篇分为两个部分的文章介绍了 Xerces-C++ XML 库。在本第 2 部分中,Rick Parrish 展示了如何装载、操作或者合成一个文档对象模型(DOM)文档,以及如何用可伸缩矢量图形(SVG)重新创建第1部分中的条形图。C++ 程序员阅读这些文章 之后应该可以在他们的应用程序中容易地增加 XML 解析和处理能力。
在 第 1 部分,您看到了如何将库链接到在 Linux 和 Windows 中编写的应用程序,以及如何用 SAX API 进行解析。一个示例应用程序展示了如何创建一个 ASCII 艺术条形图。

下面是对 DOM 节点组织的描述,然后是装载和解析以从文件或者流中产生一个 DOM 文档,合成以编程方式产生 DOM 文档,序列化或者将 DOM 文档作为输出写到文件或者流中。

如果您使用过版本 2.0 以前的 Xerces-C++,那么要当心!有些地方改变了。最显著的改变是将大多数 DOM 类对象的名字从 DOM_ 前缀重新命名为 DOM 前缀,以及选择传递指针而不是引用。

DOM 节点类型

DOM 的基础数据类型是 DOMNode 类。所有 DOM 节点对象都是从这个基类扩展来的。表 1 显示了 DOM 节点类型。第一列是由方法 DOMNode::getNodeType() 返回的枚举的类型名。第二列是用于声明这一特定节点类型的实例所使用的 C++ 类。第三列显示用于实际生成这个实例所使用的构造方法。

表 1. DOM 节点类型 DOM 类型  DOM 类  构造方法 
DOMNode::TEXT_NODE DOMText createTextNode
DOMNode::PROCESSING_INSTRUCTION_NODE DOMProcessingInstruction createProcessingInstruction
DOMNode::DOCUMENT_NODE DOMDocument createDocument
DOMNode::ELEMENT_NODE DOMElement createElement
DOMNode::ATTRIBUTE DOMAttr createAttribute
DOMNode::ENTITY_REFERENCE_NODE DOMEntityReference createEntityReference
DOMNode::CDATA_SECTION_NODE DOMCDATASection createCDATASection
DOMNode::COMMENT_NODE DOMComment createComment
DOMNode::DOCUMENT_TYPE_NODE DOMDocumentType createDocumentType
DOMNode::ENTITY_NODE DOMEntity createEntity
DOMNode::NOTATION_NODE DOMNotation createNotation


注意在 Xerces-C++ 版本 2 中,节点类型 XML_DECL_NODE 已被 get/setEncoding 、 get/setVersion 和 get/setStandalone 的 DOMDocument 方法所取代。

表 1 中的所有构造方法都存在于 DOMDocument 类中。创建 DOMDocument 对象的构造方法通过强迫您经历一个 DOMImplementation 实例以避免“鸡生蛋蛋生鸡”的问题。可以通过调用 DOMImplementation::getImplementation() 方法得到 DOMImplementation 实例,这个方法声明为静态的。要创建一个文档节点,执行以下两个步骤:

DOMImplementation *pImpl = DOMImplementation::getImplementation();
DOMDocumentType* pDoctype = pImpl->createDocumentType(L"svg",
     NULL, L"svg-20000303-stylable.dtd");
pSVG = pImpl->createDocument(L"svg", L"svg", pDoctype);
 

 

 


 回页首
 

 

DOM 装载和解析

清单 8 中的代码初始化解析器并将一个 XML 文档作为 DOM 树装载。它使用一个 DOMParser 对象完成所有这些工作。解析的工作完成后,对 getDocument() 的调用返回得到的 DOM 树。

解析器可以抛出三种异常——DOM、SAX 或者 XML——所以我在清单 8 中加入了 stubbed 异常句柄。另外一个检查错误的地方是 DOMParser::getErrorCount 函数。

 

 


 回页首
 

 

合成

清单 9 中的示例代码通过调用 DOM API 建立、或者 合成一个XML文档。所建立的这个文档是一个小的 XHTML 页面,但是事实上可以是任何 XML。为了证实事实上产生了 XML 文档,代码在控制台中显示了这个 XML 文档、标记和所有内容的内存映象(在后面我会描述在控制台显示文档所需要的额外代码)。

在阅读清单 9 中的这些代码时,注意它是如何使用 DOM 文档对象创建另一个节点的。还要注意每一个节点是如何必须显式附加到其父节点上的。即使是根节点也必须用 appendChild 方法显式附加到文档上。

 

 


 回页首
 

 

序列化

在一个实现了 DOM API 的库中,您会希望具有将 DOM 文档持久化到一个加入到库的文件或者流中的能力。在基类 XMLFormatTarget 和 XMLWriter 中描述了这种能力。在名为 XMLWriter 、 LocalFileFormatTarget 和 StdOutFormatTarget 的类中它的实现被隐藏起来了。

清单 10 中的代码创建一个用于写入文件或者标准输出流的 XMLFormatter 对象。 XMLFormatter 对象处理不同字符集的代码转换,还负责可能包含保留的 XML 字符的文本的替换。 XMLWriter 对象遍历 DOM 树、将 XML 数据块传递给格式器以便输出。使用特别的 dump_xml 检查 DOM 内容的不利之处是缺少元素之间的换行。尽管这不是必需的——或者在一些情况下甚至是不希望的——但是对于生产数据,额外的空格会使 XML 在调试期更具有可读性。下面的只有两行的清单解决了上面代码的这个问题,它添加的空格提供了可读性,但是又不会使文本节点挤满 DOM 树。


清单 11. 打印效果好的 XML
  // turn on serializer "pretty print" option
  if ( pSerializer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true) )
   pSerializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true);
 


将清单 10 和 11 放在手边以备调试。即使不需要创建一个 XML 文档,也会发现取得工作的 DOM 子树的快照的能力是很有用的。如果花时间下载本文的示例代码(见 参考资料),那么您会发现将 UTF-16 数据写入文件的宽字符版本。在 Xerces-C++ 以前的版本中,我惊讶地发现无法为“UTF-16”编码创建一个 XMLFormatter 对象,但是为“UTF-16(LE)”创建这个对象却可以成功。这个问题在版本 2 中得到了解决。

 

 


 回页首
 

 

DOM 遍历

清单 11 中的 DOMPrint 代码显示了如何访问 DOM 树中的每一个节点。清单 12 显示了另一种方式:使用迭代器及树遍历器达到同样的目的。清单 12 中的节点迭代器代码假定已经存在有效的 DOM 节点变量 root 。


清单 12. 创建一个迭代器以访问所有文本节点
// create an iterator to visit all text nodes.
DOMNodeIterator iterator =
  doc.createNodeIterator(root, DOMNodeFilter::SHOW_TEXT, NULL, true);
// use the tree walker to print out the text nodes.
for ( current = iterator.nextNode(); current != 0; current = iterator.nextNode() )
  // note: this leaks memory!
  std::cout << current.getNodeValue().transcode();
std::cout << std::endl;


清单 12 中的例子所做的就是穿过整个文档,提取文本节点并显示它们。注意 wcout ,它是 cout 的宽字符版本。清单 13 是 tree-walker 代码,它同样假定已经存在有效 DOM 节点变量 root 。


清单 13. 创建一个遍历器以访问所有文本节点
// create a walker to visit all text nodes.
DOMTreeWalker walker =
  doc.createTreeWalker(root, DOMNodeFilter::SHOW_TEXT, NULL, true);
// use the tree walker to print out the text nodes.
for (DOMNode current = walker.nextNode(); current != 0; current = walker.nextNode() )
  // note: this leaks memory!
  std::cout << current.getNodeValue().transcode();
std::cout << std::endl;


清单 13 中的 tree-walker 例子的作用与这个实例中的节点迭代器一样,因为它不使用树遍历器的任何附加功能。在用 createTreeWalker 第一次创建树遍历器时, getCurrentNode() 方法返回根节点,不管 filter 或者 to-show 设置是什么。只有在第一次调用 nextNode() 之后, getCurrentNode() 才会像预期那样操作。

 

 


 回页首
 

 

DOM 操作

DOM API 使您可以像树木修理工那样修整、嫁接和剪掉 DOM 树的节点。操作 DOM 树的方法与从头开始合成一个 DOM 树的方法相同。清单 14 总结了这些方法。


清单 14. DOMNode 方法总结
DOMNode cloneNode(bool deep) const;
DOMNode insertBefore(const DOMNode &newChild, const DOMNode &refChild);
DOMNode replaceChild(const DOMNode &newChild, const DOMNode &oldChild);
DOMNode removeChild(const DOMNode &oldChild);
DOMNode appendChild(const DOMNode &newChild);
DOMNode insertBefore(const DOMNode &newChild, const DOMNode &refChild);
DOMNode replaceChild(const DOMNode &newChild, const DOMNode &oldChild);


节点特定的创建方法如 createTextNode 和 createElement 只在 DOMDocument 对象中可用。不过,可以在任何 DOMNode 对象中使用 cloneNode 方法。

DOMElement 节点有几个额外的方法用于处理嫁接和剪除属性,如清单 15 所示。


清单 15. DOMElement 方法总结
void setAttribute(const DOMString &name, const DOMString &value);
DOMAttr setAttributeNode(DOMAttr newAttr);
void setAttributeNS(const DOMString &namespaceURI, const DOMString &qualifiedName,
     const DOMString &value);
DOMAttr removeAttributeNode(DOMAttr oldAttr);
void removeAttribute(const DOMString &name);
void removeAttributeNS(const DOMString &namespaceURI, const DOMString &localName);


从一个绑定到 DTD 的元素中删除一个属性时如果不当心,有时会产生意想不到的结果。如果这个 DTD 定义了属性的默认值,那么这个属性就会出现在 DOM 树中,而不管产生它的原始 XML。如果用 DOM API 从 DOM 树中剪除这个属性,那么它就会换成其默认值。换句话说,一个有默认值的属性节点是无法删除的!

说的差不多了,该用这些知识做一些有用的工作了。虽然基于 SAX 的图形应用程序是可用的,但是它给人的印象不是那么深刻。既然现在您已经可以使用 DOM API 了,那么就可以生成并解析 XML 了。为了加强前面的条形图的视觉效果,用 DOM 生成一个可伸缩矢量图(SVG)版本,它适合于在像 Adobe 插件、W3C 浏览器 Amaya 或者 Mozilla 的 SVG 版本(包括 1.0 及以后版本)这样的 SVG 查看器中显示。

用同样的 XML 数据作为输入,输出看起来类似于清单 16。


清单 16. 示例 XML/SVG 输出
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg SYSTEM "svg-20000303-stylable.dtd">
<svg width="320" height="200">
<g style="font-size:14">
  <rect x="100" y="20" style="stroke:node; fill:red" width="20" height="20"/>
  <text x="20" y="35">North</text>
</g>
<g style="font-size:14">
  <rect x="100" y="40" style="stroke:node; fill:blue" width="100" height="20"/>
  <text x="20" y="55">South</text>
</g>
<g style="font-size:14">
  <rect x="100" y="60" style="stroke:node; fill:yellow" width="27" height="20"/>
  <text x="20" y="75">East</text>
</g>
<g style="font-size:14">
  <rect x="100" y="80" style="stroke:node; fill:violet" width="23" height="20"/>
  <text x="20" y="95">West</text>
</g>
<g style="font-size:14">
  <rect x="100" y="100" style="stroke:node; fill:orange" width="75" height="20"/>
  <text x="20" y="115">Central</text>
</g>
<text x="20" y="135" style="font-size:10">Reported figures are preliminary only.</text>
</svg>


通过输出 SVG 兼容的 XML 标记,可以使用 SAX 来实现这个效果。注意在 SVG 输出中包括 "!DOCTYPE" 声明。文档类型给 SVG 查看器很重要的线索,表明期望它们使用哪一个版本的 SVG 技术推荐。使用一种巧妙的技巧,可以让 Xerces-C++ 在文档输出中加入 DOCTYPE。图 2 显示清单 16 中的 SVG 在查看器中的样子。


图 2. SVG 输出的截图
 

下面, 清单 17显示了加入 XML 源数据以生成 SVG 最终结果的 DOM 代码。

在 SVG 输出文档中加入 "!DOCTYPE" 声明的技巧是使用 DOMDOMImplementation::createDocument() 而不是 DOMDocument::createDocument() 来创建文档。该代码在静态函数 doc2svg 靠近开始的地方。使用 DOMDOMImplementation 使您有机会创建一个 DOMDocumentType 节点,可以在创建方法中加入这个节点。这个创建方法的 DOMDocument 版本不提供指定文档类型的方法。

可以用 DOMDocument::createDocumentType() 创建方法创建一个 DOMDocumentType 节点。这个方法在这里没什么用,因为它不允许设置 DOCTYPE 的 system ID 或者 public ID。这种技巧的另一个巧妙之处是它为您创建顶级根元素。这就是为什么代码可以调用 getDocumentElement() 而不是为文档对象显式创建并附加根元素。

 

 


 回页首
 

 

结束语

在本文及前面第 1 部分中,您已经看到 Xerces-C++ 库的好处包括开放源代码、可移植性和社团支持。可以通过解析源代码来解析库的操作。可以部署到任何支持 C++ 编译器的平台上。Windows 程序员可以得到在 C++、Visual Basic甚至 VBScript/JScript 中可以使用的 COM 版本。Apache 许可证允许在向用户做一个简单的法律声明和放弃声明的条件下,将 Xerces-C++ 用于商业目的。可以与邮件列表中的其他开发人员共同探讨有关开发的问题。所有这些好处使 Xerces-C++ 成为一个在自己项目中添加 XML 能力的绝好选择。

 


参考资料

您可以参阅本文在 developerWorks 全球站点上的 英文原文.


下载 源代码 以及本文中的图。

 


在本文作者的“ 充分利用 Xerces-C++,第 1 部分”中,学习将库链接到在 Linux 和 Windows 中编写的应用程序,以用 SAX API 进行解析。一个示例应用程序显示了如何创建一个 ASCII 艺术的条形图。

 


寻找更多有关 IBM 的 XML4C++ 解析器项目的信息,这个项目基于 Xerces-C++,可以在 alphaWorks上找到。

 


从 Apache 站点下载 Xerces-C++ XML 解析器。在那里还可以阅读 Xerces 编译文档。

 


阅读 Apache 软件许可证 条款,该许可证管理您在自己的应用程序中对 Xerces-C++ 的使用。

 


阅读有关内容或者下载 Apache XML 项目的 Xalan-C++,这是一个 XSLT 转换引擎。

 


了解 Apache 发起的其他 XML 项目。

 


阅读 SourceForge 上的 SAX API规范。

 


学习 Benoit Marchal 的 SAX, the Power API 一书中有关 SAX 的章节( developerWorks, 2001年8月),或者使用基础教程“ 理解 SAX”( developerWorks, 2003年7月)。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Feisy/archive/2008/03/26/2219324.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值