Chinaunix首页 | 论坛 | 博客
  • 博客访问: 337622
  • 博文数量: 282
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 3260
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-26 14:38
文章分类

全部博文(282)

文章存档

2011年(1)

2008年(281)

我的朋友
最近访客

分类: 服务器与存储

2008-07-27 11:04:33

 XML 的无处不在和不断改进的工具支持所创造的吸引力已经吸引了各地的组织。随着这些组织转向XML,他们必须提供一个一致的数据迁移策略让他们的用户继续使用旧的文件。这是一个不一般的问题,传统 的做法需要用枯燥的编码来实现转化过程。然而 Microsoft® .NET 框架中的 System.Xml 名字空间通过可扩展的 API 和对 XSLT 的支持极大地简化了数据迁移的难度。
  一个数据迁移的例子就是现在围绕系谱(genealogy)数据格式所发生的事情。系谱专家长期依赖GEDCOM (Genealogical Data Communications)5.5 数据格式来共享系谱信息。GEDCOM 5.5 是基于文本的而不是基于 XML 的。现在已经有了 GEDCOM 6.0 的测试版本,是完全基于 XML 的。但是 仍然还存在上千兆字节的 GEDCOM 5.5 格式信息,如何处理它们呢。这就给我们提出了一个实在是无法回避的有趣的数据迁移挑战。
  在本专栏里,我将带领你用 System.Xml 来解决这个数据迁移问题。这个过程也可以作为你将要面对的其它数据迁移问题的一个蓝图。

GEDCOM 101

  GEDCOM 被开发用来在不同的系谱程序和系统之间进行数据交换。类似 GEDCOM 的通用格式允许用户之间实现共享而不管他们正在使用的程序是什么。
  在各种不同的 GEDCOM 版本规范中,5.5 版本用的是最为广的一个版本(见 )。GEDCOM 5.5 依赖于一个简单的基于文本的 语法,使用行分分割符何分级数字来结构化家族树信息。Figure 1是一个简单 GEDCOM 5.5的文件。
  每个 GEDCOM 行包含了一个分级数字,标签,和可选值。多行组成了一个 GEDCOM 记录。0 级数字标志着一个新的 GEDCOM 记录的开始。接下来的每一行都是这个记录的一部分直到你遇到下一个 0 级行。标签的名字表达了该行的信息,它在规范中有定义。
  在 Figure 1 的例子中,HEAD 是第一个记录,它包含六个子 项(SOUR,DATE,GEDC,CHAR,SUBM,SUBN)。SOUR 包含两个子项(VERS 和 NAME)而 DATE 包含一个子项(TIME)。 递增的分级数字表明了父子关系。一行也可能包含有一个唯一的标识符,如下面片段所示:

	0 @SUB01@ SUBM
	1 NAME Aaron Skonnard
	1 ADDR 456 Main
	2 CONT Salt Lake City, Utah 84150	
  在这个例子中,SUBM记录有一个叫 SUB01 的唯一标识符。这个ID在整个文档中必须是唯一的。标识符使记录之间建立链接关系成为可能。比如,在下面的一行代码中HEAD记录引用了SUBM记录:
	1 SUBM @SUB01@
  这些是 GEDCOM 5.5 的基本语法。GEDCOM 5.5 通过使用分级数字,标识符和交叉引用就能够表达复杂的树型结构。在本文中我没有更多的 篇幅来更详细描述 GEDCOM 语义。但我的描述足以说明 GEDCOM 5.5 可以用来表达更广泛的系谱关系信息。
  既然 GEDCOM 主要被设计用来通过互操作的方式来描述树结构(家族树),那么把 GEDCOM 转移到 XML 格式似乎看起来更完美。

映射 GEDCOM 到 XML

  GEDCOM 5.5 可以很好的映射到XML。一种方法就是定义一个简单的 XML 映射,用同样的名字来转化每一个 GEDCOM 行到 XML 元素。每行的可选数据,如果有的话,可以作为“value”属性被放置。 标识符可以作为“id”属性,对其它记录的引用可以作为“idref”属性。Figure 2 可以帮助你可视化这种映射。对 Figure 1 中的 GEDCOM 5.5 文件应用这种简单映射就会产生 Figure 3 所示的XML文档。


Figure 2 映射 GEDCOM 5.5 到 XML

  XML 版本传递了同样的信息,但现在你可以用更广范围的工具和技术处理它。比如,你可以用你所喜欢的 XML API(像 DOM,SAX,XmlTextReader,或者 XPathNavigator),查询语言像 XPath 或XQuery,或者一个转换语言像 XSLT 等来处理这个 XML 文件。最终,一旦你有了一个 XML 格式的GEDCOM 数据,你就能够做任何有关它的事情了。

设计转换

  那么你如何将 GEDCOM 5.5 文件转换成 XML 格式呢?你不能使用 XSLT,因为作为输入格式,GEDCOM 5.5 不是基于 XML 的。因此你必须写代码来手工的解析 GEDCOM 5.5 的格式并产生 Figure 3 所示的 XML 格式。使用 .NET 框架,同时实现这些任务的最简单方法就是写一个自定义的 XmlReader类。
  我已经提供了一个完整的例子来解释如何完成这一切(你可以在本文的开始链接处下载该例子)。该例子包含了几个一起工作的类,它们将 GEDCOM 5.5 信息转化 成一个逻辑的 XML 文档。一开始我定义了一个其它几个类都要实现的 IGedcomNode 接口。这个接口模型化一个逻辑 XML 节点,该节点包含了来自于 GEDCOM 源的信息。它允许客户访问节点的 XML 名字,值和类型,就像出现在每个 GEDCOM 行的 GEDCOM 5.5 分级数字一样。然后,为了算出某个节点何时结束及一个新节点何时开始,我需要用到分级数字。
  接着我定义了几个具体的类来对不同类型的 XML 节点进行模型化,它们可以拼凑出 XML 文档(见 Figure 4)。它们包括 GetcomElement,GetcomAttribute 和 GetcomText。每个这样的类实现了 IGetcomNode 接口。我 还定义了一个 IGetcomLine 类,目的是管理来自某个 GETCOM 行的一系列IGetcomNode 对象。

GetcomReader 的实现

  真正的实现在 GetcomReader 类中。GetcomReader 派生于 System.Xml.XmlReader 并重载了其抽象成员。GetcomReader 的基本目的就是仔细的解析潜在的 GEDCOM 5.5 文件并把它变为 XML 文档。因此,客户不必知道有关 GEDCOM 5.5 的任何事情,只需要熟悉 Figure 3 所示的 XML 格式。
  我的 GedcomReader 实现只有一个构造函数,它使用 GEDCOM 5.5 文件名作为参数。构造函数使用 System.IO.StreamReader 对象打开给定的 GEDCOM 5.5文件并准备开始解析它。它 还提供了一个 Close 方法,当用户完成时来关闭使用中的文件流。
  Read 方法(见 Figure 5)做了大 量的工作,因为它要在整个文档中驱动逻辑指针。因此 Read 的实现必须处理 GEDCOM 5.5 格式和要模拟的目标 XML 格式(见 Figure 3)之间的转换。
  该实现使用栈跟踪当前的 XML 节点及其祖先。随着指针在当前文档中的移动 Read 会不断的压入节点到栈中,并最终弹出它们。栈中最顶层的节点被认为是当前节点。
  Read 的实现首先检查 ReadState 是否第一次调用 Read。如果是,就创建一个名为“GEDCOM”的XML 元素(使用一个 GedcomElement 对象)并压入栈中——这是目标格式(见 Figure 3)的根元素。接着就设置 ReadState 为“InterActive”。
  将来调用 Read,该实现会从潜在的 GEDCOM 5.5 文件中读取一行并调用 ParseGedcomLine,它会产生一个包含几个 IGedcomNode 对象的 GedcomLine 对象。GedcomLine 对象起缓冲作用。这是必要的因为每次调用 Read 只处理一个 XML 节点。那么在一行被解析后,将来的调用会处理残存在 GedcomLine 对象的节点 ,直到到达被缓冲的集合末尾。到此一个新行被解析,并重复处理同一过程。
Read 必须处理的另外技巧就是以正确的顺序插入 XmlNodeType.EndElement节点。

解析 GEDCOM 5.5

  解析代码(见 ParseGedcomLine)负责处理 GEDCOM 5.5 解析细节并使用不同的 IGedcomNode 派生类产生正确的 XML 格式。你可以在这个方法中改变代码来定义目标 XML 格式。Figure 5 中的代码产生的是 Figure 3 所示的 XML 格式。
  随着该代码的替换,重载其余的 XmlReader 成员变得相当简单了(见 Figure 6)。比如,LocalName 和 Name 的实现就是从栈顶中拿出当前节点和通过 IGedcomNode 接口请求它的名字。对 NodeType 的重载可以完成同样的事情。正如你所看到的,核心转换功能是在 Read 实现中 的。对于其余实现的更多细节你可以下载完整的代码参考。

使用 GedcomReader

  随着整个 GedcomReader 的实现,现在你可以处理 GEDCOM 5.5 文档了,就像它们真的是类似 Figure 3 所示 的结构化的 XML文档。比如,你可以写一个基于 XmlReader 的代码,改代码流式整个文件并一次检查一个 XML 节点:
	GedcomReader gr = new GedcomReader("skonnord.ged");
	while(gr.Read())
    	    Console.WriteLine("(0): (1)", gr.NodeType, gr.Name);	
  或者你把整个文档载入为 XmlDocument 并使用 XPath 处理它。比如下面的代码片段识别了 INDI元素,它有一个包含 Bernt 值的 NAME 元素。
	GedcomReader gr = new GedcomReader("skonnord.ged");
	XmlDocument doc = new XmlDocument();
	doc.Load(gr);
	gr.Close(); // done using GedcomReader
	XmlNode indi = doc.SelectSingleNode(
   	   "/GEDCOM/INDI[contains(NAME/@value, ''Bernt'')]");
在载入 GEDCOM 5.5 源为一个 XmlDocument 对象后,你可以使用Save方法序列化它到 XML 1.0:
	GedcomReader gr = new GedcomReader("skonnord.ged");
	XmlDocument doc = new XmlDocument();
	doc.Load(gr);
	gr.Close(); // done using GedcomReader
	doc.Save("skonnord.xml");	
  产生的 skonnord.xml 文件看起来就像 Figure 3 所示的文件。载入 GEDCOM 5.5 文件并保存得到一个文件转换。你甚至可以使用 XSLT 做进一步的转换来转化中间 XML 格式到其它东西。

对于 GEDCOM 6.0 的 XSLT

  最新的 GEDCOM 规范是 6.0 版,它为 GEDCOM 信息定义了一个完全成熟的 XML 格式。该规范甚至提供了一个文档类型定义(DTD)定义了组成整个 GEDCOM 6.0 词汇的元素和属性。
  GEDCOM 6.0 的格式非常不同于我的 GedcomReader 模拟。为了迁移到 GEDCOM 6.0,你必须修改 GedcomReader 的实现来模拟新的格式或者写一个 XSLT 在接下来的步骤中执行转换。
  GEDCOM 6.0 格式要比我在 GedcomReader 中模拟的简单映射复杂得多。后果就是在 GedcomReader 代码中试图模拟 GEDCOM 6.0 会极其困难并容易发生错误。使用 XSLT 转换来完成这些步骤会更加容易 处理问题。
  在 Figure 7 中是我在例子工程中提供的一个 XSLT,它解释了如何从 Figure 3 所示的中间 XML格式产生 GEDCOM 6.0 文件。该 XSLT 覆盖了大多数通用的 GEDCOM 6.0 用例,它产生的文件通过了 GEDCOM 6.0 DTD 的验证。
  通过利用 System.Xml.Xsl.XslTransform 类的优点,你可以使用这个 XSLT,如下所示:
	GedcomReader gr = new GedcomReader(gedcomFileName);
	XmlDocument doc = new XmlDocument();
	doc.Load(gr);
	gr.Close(); // done using GedcomReader

	XslTransform tx = new XslTransform();
	tx.Load("gedcom6.xls");
	FileStream fs = 
   	    new FileStream("skonnord6.xml", FileNode.Create);
	tx.Transform(doc, null, fs, null);
  输出文件,skonnor6.xml 现在与 GEDCOM 6.0 兼容。此时编写其它的 XSLT 转换将中间 XML 格式改变成你想要的其它格式也 是可能的。比如,你可以写一个 XSLT 转换产生人类可读的 HTML 页来查看和导航 GEDCOM 家族树信息。
  正如你所看到的,实现一个完整的 GEDCOM 迁移路径不需要太多的代码。GEDCOM 5.5 和 GEDCOM 6.0 之间的可编程迁移能力极大地简化了迁移 情节,该情节牵涉到用新的 GEDCOM 6.0 数据模型构建一个完整的系谱系统。

我们在哪里?

  我已经完整地讲述了一个使用 .NET 中 System.Xml 类对真实数据迁移的细节。从 GEDCOM 5.5 格式开始到能够用不同方法处理的中间 XML 格式。通过一个名为 GedcomReader 的 自定义的 XmlReader 实现使这成为可能。
  中间 XML 格式还能够被转化为不同的其它格式。通过使用 XSLT 转化(见 Figure 8),我能够迁移到 GEDCOM 6.0。


Figure 8 迁移到 GEDCOM 6.0

  System.Xml 工具提供的可扩展点可以处理类似的不同数据迁移情节。如果你拥有旧的数据格式,可以仿照本文中所讨论的方法来实现你自己的迁移路径。 将它迁移到 XML,你会从中受益的。

作者简介

  Aaron Skonnard 在盐湖城的 Northface 大学执教。Aaron 与人合著有《 Essential XML Quick Reference 》(Addison-Wesley, 2001)和《Essential XML》(Addison-Wesley, 2000),并经常在一些会议上演讲。 他的联系方式是
 
本文出自 的 期刊,可通过当地报摊获得,或其最好是

本文由 翻译
阅读(322) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~