技巧:用面向对象编程创建有效的XML

2002-8-22 10:42:43【作者】 畅享网 【进入论坛】
广告

技巧:用面向对象编程创建有效的XML

--将OOP数据压缩到XML规则中


David Mertz,博士(
mertz@gnosis.cx

Gesticulator, Gnosis Software, Inc.

2002 年 3 月

在这篇技巧文章中,David 提供了一些技巧,使用面向对象语言的程序员可以在他们的程序中使用这些技巧以在创建 XML 文档时确保 XML 文档的有效性。

XML 具有某种双重标识。一方面,在其 SGML 根源方面,XML 是一种表达规范和严格的结构化数据的方法;DTD 和 XML Schema 描述这些结构。另一方面,就象人们在一些流行的 API(DOM、SAX 和 XSLT,以及通过其它 XML 库)中看到的那样,XML 只是一种表示一般分层的数据的方法。遗憾的是,这两方面交流得很不好。创建有效文档超出了这些 API 的范畴(有效性通常移交给解析阶段)。然而,仅仅通过使用几个特殊的策略,您就至少可以使 OOP 对象看起来十分类似于有效的 XML。

什么构成有效性?

可以用 DTD 表达的有效性约束种类与可以用 W3C XML 模式表达的有效性约束种类有些区别。在本技巧文章中,我将集中讨论不同有效性约束规范风格所共有的问题。

XML 有效性的基本思想是指定在元素内部可以出现什么,它每隔多久可以出现一次以及可以出现的内容有哪些替代选择。另外,当在一个元素中可以出现多个事物时,还可以指定它们出现的顺序(或按照需要不指定)。下面我们看一看一个高度简化的假想的 dissertation.dtd:

具备所有基本约束的“dissertation”DTD






换句话说,dissertation 可以包含一个 dedication,必须包含一个或多个 chapter 并且可以包含零或多个 appendix。不同的子元素以列出的顺序出现(如果有的话)。一些元素仅包含字符数据。就 标记而言,它既可以包含字符数据,也可以包含一个

子元素或一个 子元素。结构可以嵌套,但是这一示例包含了每一个基本有效性概念。

使对象看起来象文档

尽管 DOM 不这样做,但 OOP 语言具有表示有效性的所有基本数据结构(一些语言比其它语言更完整)。基本说来,这里的窍门在于创建属性受约束的对象,以便仅允许某些事物进入它们。这一工作是在编译时完成还是在运行时完成取决于所使用的语言,但是这两种方式都可以引入必要的约束。在任何情况下,都必须对每种文档类型 — 而不是笼统地对所有 XML — 创建正确种类的对象。

XML 文档是分层的,类似的,可以包含其它对象作为属性的对象也是分层的。对于不同的属性,我们仅需选择几种对象类型。

在文档根处 — 以及在其之下的其它级别上 — 我们要指定可以出现的子元素及其出现的顺序。我想起了两种方法。一种方法是创建可以在该元素中出现的那些事物的不同种类的序列。Haskell 和 Python 元组具有保持不变的良好品质(例如,象 Fortran 记录那样),但是常规列表或数组(例如 void 指针)也可以这样做。例如,在 Ruby 中,我们可能会“声明”:

用成员“part”初始化 Ruby“Root”类 dissertation = Root.new [ Maybe_dedication.new, \
Some_chapter.new, \
Any_appendix.new ]
# Example of accessing part of root element sequence
dissertation.part[1].addchapter(some, arguments, here)

该示例完全没有说明类 Maybe_dedication、Some_chapter 和 Any_appendix 做什么,但是包含序列思想。dissertation 是类 Root 的实例,其工作是保存一个序列(可能还有其它几个方法,如写 XML 或验证当前实例)。

第二种方法在很多语言中要更简单一些,它使事物直接以根元素属性出现,但是保留一个指示序列的特殊属性。例如,Java 可能做类似于下面的事:

将 dissertation 部分作为成员保留的 Java 类 public class dissertation {
Maybe dedication = new Maybe(dedication_type);
Some chapters = new Some(chapter_type);
Any appendices = new Any(appendix_type);
Seq _seq = new Seq[] {dedication, chapters, appendices};
}
/* Access is somewhat more natural now */
dissertation.chapter.add(some, arguments, here);

表达量化

我们已经看到可以允许元素的各个“部分”出现的次数不同:一次或零次;一次或多次;零次或多次。我们需要对象来表示这些数量。可能性情况(零次或多次)很简单,可以由标准(同类)序列表示:列表、数组、向量或编程语言所称呼和提供的任何东西。为了提供其它行为,将一个基本序列封装在一个定制类中可能是值得的。

限制情况(一次或零次,也就是说不多于一次)的结构不太明显。最可能的情况是,应该在基本序列对象的基础之上构建 Maybe 类(以及象 Maybe_dedication 这样的子类),并且在提供的方法中有防止添加一个以上事物的保护手段。这类似于我们存在情况(一次或多次)的示例。那么,让我们看一看可能(不完整)的 Python 实现:

拥有“一次或多次”事物的 Python 类

class ExistentialList(UserList.UserList):
def __init__(self, tag=None, initlist=[]):
if tag is None:
raise ValueError, "Must specify tag name"
self.tag = tag
UserList.UserList.__init__(self, initlist)
def __delitem__(self, i):
del self.data[i]
self.validate()
def __repr__(self):
self.validate()
return'\n'.join(['<%s>%s' % (self.tag, item, self.tag)
for item in self.data])
def validate(self):
ifnot len(initlist):
raise"ExistentialError", "List must have length >= 1"

更健壮的示例可能会在 .append()、.extend()、.__setitem__() 和其它可以添加到列表的方法中添加一些特性,例如检查项目类型是否正确。具有静态类型定义的语言可能仅仅会在列表中声明事物类型,但可能使用类似的大小验证。在 C++ 中,您可能使用模板和通用编程方法来实现 ExistentialList 的一般特性,但是要特别指明子元素类型。

每一个表示子元素的对象的重要特性是将其本身序列化成 XML 的能力。Python 保留一个名为 __repr__() 的“神奇”方法;其它语言可能使用一个名为 .write() 或 .toXML() 的方法。序列化还需要向下递归遍历子元素,这由 Python 使用神奇方法自动处理。在其它情况下,需要对属性对象进行一些人工调用。

表达分离

出现模式之间的交替在大多数 OOP 语言中更加难以表达。有几种语言(例如 Pascal、Fortran 和最棒的 Haskell)允许枚举类型(有区别的联合),但是这些是例外,而不是规则。在大多数语言中,您必须添加定制检查以确保可允许项之一由一个实例约束。

您可能通过为构造器提供多个类型说明来编写多态构造器函数。交替模式中的每一个允许的事物都将有一个构造器(可能包括在与 DTD 中的分离一起使用的量词处的序列类型)。

然而,更灵活的方法是使用一个通用框架,该通用框架来通过初始化变量或通过 Or 超类的继承来接收其交替列表。这样的子类看起来可能类似于:

Or 抽象类的 Python 专门化

class paragraph(Or):
disjoins = (PCDATA, figure, table)

假设抽象类 Or 的基本功能知道查找 disjoins 类成员,那么这可能是足够的专门化。当然,在父类中需要定义受保护的写方法和读方法(在 Python 案例中,可能用神奇方法来实现)。

结束语

通过在数据类所包含的值上使用一些约束来使用数据类允许我们反映出 XML 文档的有效性要求。可以象构造子元素容器那样构造类容器。有了一个优秀的基类框架,基本上,您就可以将 DTD 直接复制到一组类定义或初始化中了。实例成为其它实例的成员,最终您得到一个根对象实例,该实例与其属性一起,具有以不能破坏有效性要求的方式操纵数据的多种方法(与之相反,使用象字符串和数组那样的基本类型很容易破坏有效性)。在操纵结束时,根对象应该具有某种方法写出 XML — 向下递归遍历其属性实例 — 这将产生一个完全成熟而有效的 XML 文档。例如,一个 ExistentialList 可以包含其它 ExistentialList 对象 — 嵌套的序列化(print 语句)与给定的样本合作得非常好。

最后再提个醒:虽然我没有在这里给出示例,但是 Eiffel 的“programming-by-contract”非常适合于表示本文所讨论的约束。

参考资料

关于作者

David Mertz 运用完全非结构化的头脑来编写结构化的文档格式。可以通过 mertz@gnosis.cx 与 David 联系;在 http://gnosis.cx/publish/ 上了解他的生活。


如果您希望与本文章的作者或其所在机构,进一步交流,请联系:畅享网 姜小姐
jill.jiang@amteam.org | 021-51096826-112 | 在线联系
老孙的IT运维管理之道[原创]用户的BSM用户的IT业务管..

从企业实际的IT运营角度来看,BSM是推动IT与业务融合,实现、改善WCNG司IT管理和治理的最佳实践之一。

吕建伟 专栏和CIO问答软件项目实施管理

现实中很少能按照正规流程来的,所以只能把流程中的各个环节拆开,个个击破,以后就可以见招拆招了。

节能与优化IT 企业CIO过冬良策

当前金融危机的影响还在继续漫延,很多企业都在苦寻过冬的良策,在这种情况下,节能与优化技术与产品无疑成为CIO们关注的首要对象,本次选题就是针对节能与优化IT来为CIO们提供过冬的良……

观08软件并购风潮 议09巨头何处生花

2008,似乎注定是不平静的一年。有人说2008是并购年。业内人士表示,在全球软件行业,并购一直是大企业谋求做大做强的捷径之一,包括甲骨文、SAP,微软等全球软件巨头都为了扩大自己……