|
以文本方式查看主题 - 中文XML论坛 - 专业的XML技术讨论区 (http://bbs.xml.org.cn/index.asp) -- 『 DTD/XML Schema 』 (http://bbs.xml.org.cn/list.asp?boardid=23) ---- 比较 XML 文档语义等价性的一些建议 (http://bbs.xml.org.cn/dispbbs.asp?boardid=23&rootid=&id=11925) |
|
-- 作者:anchen0617 -- 发布时间:11/9/2004 3:17:00 PM -- 比较 XML 文档语义等价性的一些建议 您如何区分两个 XML 文档是否相同?Brett McLaughlin 解释了为什么回答这个一般性问题不是那么简单。这个解释告诉我们如何来比较 XML 文档,包括如何处理一些重要空格以及可忽略的空格和外部实体引用。代码样本包含了 DTD 和 SAX EntityResolver 示例。本文认为您已经具备了有关 XML 的基础知识和并理解 SAX 的概念。 最近我尝试回答关于如何比较 XML 文档,从而发现它们是否相同的一个简单问题。回答并不是如此简单,因为它涉及到语义等价方面的问题。 由于 XML 所具有的灵活性(这正是 X 所代表的可扩展,记得吗?),同一个数据可以用多种方式来表示。因此当您想发现两个文档是否“代表”同一个事物,即是否在 语义上等价时,事情就显得有点错综复杂了。在本文中,我研究了这个问题,并向您展示了处理这类比较的一些技术,以及大致地告诉您关于 XML 等价性问题。所以系紧安全带;这里有一点颠簸! 这些是不同的 首先最常见的方法是用类似于 diff(在大多数 *NIX 系统中都有)的标准实用程序来比较 XML。所以可以拿两个文档并用 diff 程序来比较它们。如果实用程序报告没有区别(一般来讲,这意味着没有任何反馈回送到命令行或 shell 提示符),那么表示这两个文档是相同的, 然而,这种情况很少发生。大多数情况下,用 diff 程序比较 XML 文档会产生数行(或几十行甚至数百行)反馈结果。在文档中有类似于这种许许多多行的“有区别”的内容,这意味着:它们是不同的,对吗?唔……看来您还真的不知道这是怎么回事。这些有区别的反馈结果实际上刚刚使您开始涉足 XML 比较。 当 diff 不能胜任时 清单 1. document1.xml <?xml version="1.0"?> <hockeyTeam>
很简单。现在看一下清单 2 中的 dcument2.xml。 清单 2. document2.xml <?xml version="1.0"?> <hockeyTeam>
当您研究这两个短小的 XML 文档时,恐怕已经注意到清单 1 和清单 2 所表示的含义几乎没有什么不同。每个元素的文本数据都相同,不是吗?如果将这两个文档读入使用 SAX、DOM 或 JDOM 的应用程序,您确实会得到相同的数据结果。那么当使用 diff 命令来比较这两个简单的、几乎相同的文档时,为什么会得到大量的反馈结果呢?这是由于在两个 XML 文档之间,有一些区别是重要的,而有一些则不是,但 diff 实用程序自身不足以区别这一点。 准备,开始…… 首先,需要将清单 1、2 和 3 中的 XML 文档保存在本地,从而可以对它们进行语法分析。同时,需要有 Java 编译器、XML 语法分析器,有可能还需要 XML 编辑器。 顺便说一句,这里认为您已具备了 XML 的基本知识。在这里将不会花大量的笔墨来讲述本文中所讨论的诸如元素、DTD 和实体引用等。(如果您对 XML 完全是一个新手,请参阅参考资料中有关 XML 的背景和介绍性材料。)从具有一些基本的 XML 背景知识到能够理解有关空格和实体解析的问题,还有一段漫长的探索过程。除了了解 XML 基础知识,您至少还需要理解 SAX 的概念,从而能采用本文中的一些建议。这里不需要您是 SAX 的奇才,但您必须熟读 SAX Javadoc (请参阅参考资料),这会使您受益匪浅。这里我还提到了其它两个 XML API:DOM 和 JDOM。当然您不一定要了解这些 API,但想重申一下,大致了解它们会对您理解本文有一定好处。 一切都准备就绪,让我们反过头来准备学习超乎您原来想象的 XML 文档比较。 处理空格 可忽略的空格 清单 3. 在清单 1 的摘录中发现空格 <arena name="Reunion Arena">
这里的问题是关于开始 arena 标记的末尾处和 ice 元素开始处之间的空格。这里有一个换行,因此可能有一些尾随的空格。所以在 arena 的右尖括号和 ice 的左尖括号之间的内容可能是 " \n"。在下面两行的末尾也有类似空格问题。 为了使问题更明白,清单 3A 通过在空格处加下划线来标识出 arena 定义中的空格(当然,我不是真的在换行处加下划线,而是便于您理解。) 清单 3A. 为了突出空格在其下面加下划线 <arena name="Reunion Arena">_
现在回过头来看类似于清单 2 中的 document2.xml 文档,它有与清单 1 相同的元素,但空格是不一样的。在清单 2 的文档中空格是 "\n" 而不是将开始 arena 标记和开始 ice 标记分开的 " \n "。而这似乎很小的差别会给 XML 比较带来巨大的灾难。这就是差别,但它很重要吗?遗憾的是,回答是也许。 重要的空格 清单 4. 用于格式化的空格 <signature> --- Enhydra Strategist
在清单 4 中,空格很明显应该是文档的一部分,这对于文档作者来说是很重要的。这就是为什么如果没有 DTD,而让语法分析器来假定空格的含义是不安全的。所以,在尝试比较两个 XML 文档时,要首先为两者阐明 DTD。这允许您指定语法分析器可以忽略哪个空格,而哪个空格是重要的。 寻找 DTD 清单 5. 允许任何内容的 DTD <!ELEMENT hockeyTeam ANY>
这个简短的清单 5(明显)是一个 DTD,它允许在根元素 hockeyTeam 中有任何内容。这个 DTD 没有什么用处,因为很可能在 hockeyTeam 的子元素的内容中包含重要空格。所以,DTD 必须指定的是:对于给定的元素,在其中仅能包含其它元素。确切地说:任何空格都是可忽略的,因为允许的内容仅仅是其它元素。清单 6 中的 DTD 阐明了 arena 元素中可忽略的空格。 清单 6. document1.xml 和 document2.xml 的 DTD <!ELEMENT hockeyTeam (city, state, mascot, <!ELEMENT city (#PCDATA)> <!ELEMENT conference (#PCDATA)> <!ELEMENT nhlCopyright ANY>
所以,当比较 XML 时,您需要阐明 DTD 来尽可能准确地约束正在比较的文档。特别是,如果一个元素仅能包含其它元素,那么一定要在 DTD 中表明。这种精确性可以确保在与类似 SAX、DOM 和 JDOM 这样的 API 一起使用时可以忽略文档中的任何空格。在 SAX 中,元素中的空格不会报告给 characters() 回调(它是用来报告文本元素内容的方法)。而是将元素中的任何空格报告给 ignorableWhitespace() 回调,这就不用担心了。当然,这是一件好事情。 现在您已经知道了比较两个“相似”XML 文档的第一步:定义 DTD。然后让两个 XML 文档都引用这个 DTD,这样就可以尽可能多地区别出空格。然而,如果在此时(如当用 SAX、DOM 或 JDOM 读文档时)在空格方面仍然还有不同,那么这两个文档是不同的。除了使用 DTD 以外,再没有其它方法可以表明任何其它空格是不重要的。所以,如果在使用 DTD 后,仍有不同的空格,那么这两个文档确实是不同的。 实体解析 SAX 和 EntityResolver 为了解决这个问题,可以用 SAX EntityResolver 实现。在 org.xml.sax.EntityResolver 中定义了这个接口,并提供了一个单一方法 resolveEntity()。该方法允许您提供自己的实体解析,从而防止语法分析器使用 DTD 来做这件事。所以,对于这两个文档,可以注册一个以相同方式解析实体的 EntityResolver 实现。这从等式中除去正是您想除去的又一个比较点。清单 7 是一个总是为 NHLCopyright -- 在两个 XML 文档样本中的实体引用返回相同值的样本实现。通过查看用于实体系统和公用标识的 DTD 中指定的值,可以确保对于所有文档返回相同的值。 清单 7. 解析所有 NHLCopyright 实体 package com.developerWorks.xml.util; import org.xml.sax.EntityResolver; public class CommonResolver implements EntityResolver { public void resolveEntity(String publicID, String systemID) // Look for the NHLCopyright system ID // In all other cases, return null
从等式消去外部实体 清单 8. 对于外部实体引用带有不同标识的 document1.xml 和 document2.xml 的 DTD <!ELEMENT hockeyTeam (city, state, mascot, <!ELEMENT city (#PCDATA)> <!ELEMENT arena (ice, location)> <!ELEMENT conference (#PCDATA)>
在清单 8 中,外部实体引用的 URL 是不同的,当然,这会引起问题。虽然清单 8 中的其余 DTD 与清单 6 中是相同的(并且在进行文档空格比较时,会得出相同的结果),但外部实体引用解析是不同的,从而两个文档可能不同。为了避免这种情况,可对清单 7 的 CommonResolver 类添加新的系统标识。清单 9 修改了 resolveEntity() 方法,有效地从等式除去了两个实体的不同,从而可以再次进行有效地比较。 到目前为止,您已经解决了 XML 1.0 中可能出现的所有空格问题,而且已经分离出实体解析。现在您可以比较大多数文档,来看它们是否相同。但在得出结论之前,还有多个概念性问题值得注意。 实际相对理论 这种不同的最好示例是属性对元素的典型争论。换句话说:文档中的数据是以元素存储还是以属性存储?如果两个文档有相同的数据,但存储的方式不同,那么这两个文档是相同的?还是不同的?看一下清单 10。 清单 10. 使用属性而不是元素的 XML 文档 <?xml version="1.0"?> <hockeyTeam>
这与清单 1 中的数据是一样的,但这些数据是以属性表示而不是以元素表示。这两个文档在技术上相同吗?不,完全不同。然而,您可能争辩说,这两个文档中的数据是相同的。如果那是事实,那么您可能认为文档自身具有相同的含义。 现在,为避免我把您弄糊涂,这纯粹是理论上的讨论,不存在任何 API 可将这两个文档解释为等价的。您必须自己决定是否需要花些功夫来编写处理这两个文档的代码,但应该意识到存在的这些差别,因为某一天您可能不得不处理它们。 总结 从中寻找乐趣吧,网上见!
|
|
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
46.875ms |