新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> DTD, XML Schema(XMLS), RELAX NG
    [返回] 中文XML论坛 - 专业的XML技术讨论区XML.ORG.CN讨论区 - XML技术『 DTD/XML Schema 』 → 比较 XML 文档语义等价性的一些建议 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 2500 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 比较 XML 文档语义等价性的一些建议 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     anchen0617 帅哥哟,离线,有人找我吗?双子座1983-6-17
      
      
      威望:5
      等级:大二(研究C++)
      文章:281
      积分:3413
      门派:XML.ORG.CN
      注册:2004/10/17

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给anchen0617发送一个短消息 把anchen0617加入好友 查看anchen0617的个人资料 搜索anchen0617在『 DTD/XML Schema 』的所有贴子 访问anchen0617的主页 引用回复这个贴子 回复这个贴子 查看anchen0617的博客楼主
    发贴心情 比较 XML 文档语义等价性的一些建议

    您如何区分两个 XML 文档是否相同?Brett McLaughlin 解释了为什么回答这个一般性问题不是那么简单。这个解释告诉我们如何来比较 XML 文档,包括如何处理一些重要空格以及可忽略的空格和外部实体引用。代码样本包含了 DTD 和 SAX EntityResolver 示例。本文认为您已经具备了有关 XML 的基础知识和并理解 SAX 的概念。
    最近我尝试回答关于如何比较 XML 文档,从而发现它们是否相同的一个简单问题。回答并不是如此简单,因为它涉及到语义等价方面的问题。

    由于 XML 所具有的灵活性(这正是 X 所代表的可扩展,记得吗?),同一个数据可以用多种方式来表示。因此当您想发现两个文档是否“代表”同一个事物,即是否在 语义上等价时,事情就显得有点错综复杂了。在本文中,我研究了这个问题,并向您展示了处理这类比较的一些技术,以及大致地告诉您关于 XML 等价性问题。所以系紧安全带;这里有一点颠簸!

    这些是不同的
    简而言之,问题是这样的:您有两个(或更多)XML 文档,想知道它们是否相同。您该怎么办?

    首先最常见的方法是用类似于 diff(在大多数 *NIX 系统中都有)的标准实用程序来比较 XML。所以可以拿两个文档并用 diff 程序来比较它们。如果实用程序报告没有区别(一般来讲,这意味着没有任何反馈回送到命令行或 shell 提示符),那么表示这两个文档是相同的, 然而,这种情况很少发生。大多数情况下,用 diff 程序比较 XML 文档会产生数行(或几十行甚至数百行)反馈结果。在文档中有类似于这种许许多多行的“有区别”的内容,这意味着:它们是不同的,对吗?唔……看来您还真的不知道这是怎么回事。这些有区别的反馈结果实际上刚刚使您开始涉足 XML 比较。

    当 diff 不能胜任时
    为了更清楚地了解为什么用象 diff 这样的工具比较这些 XML远远不够,让我们看一下清单 1,它显示了一个简单的 XML 文档。

    清单 1. document1.xml

    <?xml version="1.0"?>
    <!DOCTYPE hockeyTeam SYSTEM "hockeyTeam.DTD">

    <hockeyTeam>
    <city>Dallas</city>
    <state>Texas</state>
    <mascot>Stars</mascot>
    <arena name="Reunion Arena">
    <ice quality="poor" />
    <location city="Dallas" />
    </arena>
    <conference>Western</conference>
    <division>Pacific</division>
    <nhlCopyright>&NHLCopyright;</nhlCopyright>
    </hockeyTeam>

    很简单。现在看一下清单 2 中的 dcument2.xml。

    清单 2. document2.xml

    <?xml version="1.0"?>
    <!DOCTYPE hockeyTeam SYSTEM "hockeyTeam.DTD">

    <hockeyTeam>
    <city>Dallas</city><state>Texas</state><mascot>Stars</mascot>
    <arena name="Reunion Arena">
    <ice quality="poor" />
    <location city="Dallas" />
    </arena>
    <conference>Western</conference><division>Pacific</division>
    <nhlCopyright>&NHLCopyright;</nhlCopyright>
    </hockeyTeam>

    当您研究这两个短小的 XML 文档时,恐怕已经注意到清单 1 和清单 2 所表示的含义几乎没有什么不同。每个元素的文本数据都相同,不是吗?如果将这两个文档读入使用 SAX、DOM 或 JDOM 的应用程序,您确实会得到相同的数据结果。那么当使用 diff 命令来比较这两个简单的、几乎相同的文档时,为什么会得到大量的反馈结果呢?这是由于在两个 XML 文档之间,有一些区别是重要的,而有一些则不是,但 diff 实用程序自身不足以区别这一点。

    准备,开始……
    现在您看到了为什么使用 diff 实用程序是远远不够的,在充分利用本文之前,还需要做其它一些事情。

    首先,需要将清单 1、2 和 3 中的 XML 文档保存在本地,从而可以对它们进行语法分析。同时,需要有 Java 编译器、XML 语法分析器,有可能还需要 XML 编辑器。

    顺便说一句,这里认为您已具备了 XML 的基本知识。在这里将不会花大量的笔墨来讲述本文中所讨论的诸如元素、DTD 和实体引用等。(如果您对 XML 完全是一个新手,请参阅参考资料中有关 XML 的背景和介绍性材料。)从具有一些基本的 XML 背景知识到能够理解有关空格和实体解析的问题,还有一段漫长的探索过程。除了了解 XML 基础知识,您至少还需要理解 SAX 的概念,从而能采用本文中的一些建议。这里不需要您是 SAX 的奇才,但您必须熟读 SAX Javadoc (请参阅参考资料),这会使您受益匪浅。这里我还提到了其它两个 XML API:DOM 和 JDOM。当然您不一定要了解这些 API,但想重申一下,大致了解它们会对您理解本文有一定好处。

    一切都准备就绪,让我们反过头来准备学习超乎您原来想象的 XML 文档比较。

    处理空格
    这是首要之首要。在比较 XML 文档中最重要的问题是空格处理。这是因为看上去没有任何变化的实际空格,其 含义可以变化。感到迷惑?当我第一次听到时,也是这种反应,但这里我会向您解释的。首先,告诉您什么是可忽略的空格,它是如何改变您周围的事物……呃……XML。然后我向您演示 DTD 是如何更改某个文档的含义,以及您如何使用它们来帮助您比较 XML 文档。

    可忽略的空格
    事实上,可忽略的空格是指空格可以忽略。换句话说,该空格可以丢弃而不改变文档的含义。譬如,这篇 XML 文档中的 arena 元素有两个子元素:ice 和 location。清单 3 是 document1.xml 的一段摘录。

    清单 3. 在清单 1 的摘录中发现空格

    <arena name="Reunion Arena">
    <ice quality="poor" />
    <location city="Dallas" />
    </arena>

    这里的问题是关于开始 arena 标记的末尾处和 ice 元素开始处之间的空格。这里有一个换行,因此可能有一些尾随的空格。所以在 arena 的右尖括号和 ice 的左尖括号之间的内容可能是 " \n"。在下面两行的末尾也有类似空格问题。

    为了使问题更明白,清单 3A 通过在空格处加下划线来标识出 arena 定义中的空格(当然,我不是真的在换行处加下划线,而是便于您理解。)

    清单 3A. 为了突出空格在其下面加下划线

    <arena name="Reunion Arena">_
    __<ice quality="poor" />_
    __<location city="Dallas" />__
    </arena>

    现在回过头来看类似于清单 2 中的 document2.xml 文档,它有与清单 1 相同的元素,但空格是不一样的。在清单 2 的文档中空格是 "\n" 而不是将开始 arena 标记和开始 ice 标记分开的 " \n "。而这似乎很小的差别会给 XML 比较带来巨大的灾难。这就是差别,但它很重要吗?遗憾的是,回答是也许。

    重要的空格
    在 XML 中如果没有指定 DTD,那么空格是不可忽略的。我再次重申:如果没有 DTD,空格是不可忽略的。换句话说,语法分析器会认为这两个文档是不同的。这是因为 XML 语法分析器不知道文本数据 -- 令人头疼的空格 -- 是否重要。对于您来说,它仅仅是无关紧要的空格。对于文档作者来说,它可能是在 XSL 变换中将要使用的格式。我知道,这似乎是难以相信的,但清单 4 中 XML 样本会怎样呢?

    清单 4. 用于格式化的空格

    <signature>

    ---
    Brett McLaughlin

    Enhydra Strategist
    http://www.enhydra.org


    </signature>

    在清单 4 中,空格很明显应该是文档的一部分,这对于文档作者来说是很重要的。这就是为什么如果没有 DTD,而让语法分析器来假定空格的含义是不安全的。所以,在尝试比较两个 XML 文档时,要首先为两者阐明 DTD。这允许您指定语法分析器可以忽略哪个空格,而哪个空格是重要的。

    寻找 DTD
    现在您已知道需要 DTD。但好像事情不是那么简单,不是任何 DTD 都可以做到这一点。譬如,清单 5 中的 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,
    logo, arena, conference)>

    <!ELEMENT city (#PCDATA)>
    <!ELEMENT state (#PCDATA)>
    <!ELEMENT mascot (#PCDATA)>
    <!ELEMENT arena (ice, location)>
    <!ATTLIST arena
    name CDATA #REQUIRED>
    <!ELEMENT ice EMPTY>
    <!ATTLIST ice
    quality CDATA #REQUIRED>
    <!ELEMENT location EMPTY>
    <!ATTLIST location
    city CDATA #REQUIRED>

    <!ELEMENT conference (#PCDATA)>
    <!ELEMENT division (#PCDATA)>

    <!ELEMENT nhlCopyright ANY>
    <!ENTITY NHLCopyright SYSTEM "http://www.nhl.com/nhlCopyright.xml">

    所以,当比较 XML 时,您需要阐明 DTD 来尽可能准确地约束正在比较的文档。特别是,如果一个元素仅能包含其它元素,那么一定要在 DTD 中表明。这种精确性可以确保在与类似 SAX、DOM 和 JDOM 这样的 API 一起使用时可以忽略文档中的任何空格。在 SAX 中,元素中的空格不会报告给 characters() 回调(它是用来报告文本元素内容的方法)。而是将元素中的任何空格报告给 ignorableWhitespace() 回调,这就不用担心了。当然,这是一件好事情。

    现在您已经知道了比较两个“相似”XML 文档的第一步:定义 DTD。然后让两个 XML 文档都引用这个 DTD,这样就可以尽可能多地区别出空格。然而,如果在此时(如当用 SAX、DOM 或 JDOM 读文档时)在空格方面仍然还有不同,那么这两个文档是不同的。除了使用 DTD 以外,再没有其它方法可以表明任何其它空格是不重要的。所以,如果在使用 DTD 后,仍有不同的空格,那么这两个文档确实是不同的。

    实体解析
    一旦完成了最初的空格问题,并且从这个方面来看这些文档是相同时,接着需要处理外部实体解析。让我们再看一下清单 1 中的 document1.xml 和清单 2 中的 document2.xml,它们都有外部实体引用 NHLCopyright。记住,这个实体引用是通过 DTD 引用来解析的,并且在运行时它可以转换为文本内容、更多的 XML 内容或其它内容。这又会引起问题,因为当比较文档时,您希望确保这些实体的解析是相同的。由于这两个文档可以使用不同的 DTD,因此对相同实体引用的解析可以是不同的。在这一章节,我将演示如何解决这个特定的问题。

    SAX 和 EntityResolver
    显而易见的解决方案是确保两个文档引用相同的 DTD。然而,这并不总是可能的。譬如,您只读地访问 XML 文档,或者用程序的方式比较这些文档(在当前的 API 中,变更 DOCTYPE 引用不是标准化的)。在这些情况下,需要一种方法来“短路”解析。换句话说,要确保实体引用解析为您决定的值,而不是 DTD 中的值 。

    为了解决这个问题,可以用 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;
    import org.xml.sax.InputSource;

    public class CommonResolver implements EntityResolver {

    public void resolveEntity(String publicID, String systemID)
    throws SAXException {

    // Look for the NHLCopyright system ID
    if (systemID.equals("http://www.nhl.com/nhlCopyright.xml")) {
    return new InputSource("myLocalCopyright.xml");
    }

    // In all other cases, return null
    return null;
    }
    }

    从等式消去外部实体
    您也需要考虑,对于相同的外部实体引用,两个 DTD 实际指定的可能是不同的公用和系统标识。譬如,清单 8 显示的 DTD 非常类似于清单 6,但对于 NHLCopyright 引用指定了不同的系统标识。

    清单 8. 对于外部实体引用带有不同标识的 document1.xml 和 document2.xml 的 DTD

    <!ELEMENT hockeyTeam (city, state, mascot,
    logo, arena, conference)>

    <!ELEMENT city (#PCDATA)>
    <!ELEMENT state (#PCDATA)>
    <!ELEMENT mascot (#PCDATA)>

    <!ELEMENT arena (ice, location)>
    <!ATTLIST arena
    name CDATA #REQUIRED
    >
    <!ELEMENT ice EMPTY>
    <!ATTLIST ice
    quality CDATA #REQUIRED
    >
    <!ELEMENT location EMPTY>
    <!ATTLIST location
    city CDATA #REQUIRED
    >

    <!ELEMENT conference (#PCDATA)>
    <!ELEMENT division (#PCDATA)>
    <!ELEMENT nhlCopyright ANY>
    <!ENTITY NHLCopyright SYSTEM "http://www.dallasStars.com/nhl/copyright.xml">

    在清单 8 中,外部实体引用的 URL 是不同的,当然,这会引起问题。虽然清单 8 中的其余 DTD 与清单 6 中是相同的(并且在进行文档空格比较时,会得出相同的结果),但外部实体引用解析是不同的,从而两个文档可能不同。为了避免这种情况,可对清单 7 的 CommonResolver 类添加新的系统标识。清单 9 修改了 resolveEntity() 方法,有效地从等式除去了两个实体的不同,从而可以再次进行有效地比较。

    到目前为止,您已经解决了 XML 1.0 中可能出现的所有空格问题,而且已经分离出实体解析。现在您可以比较大多数文档,来看它们是否相同。但在得出结论之前,还有多个概念性问题值得注意。

    实际相对理论
    到目前为止,已经慎密地讨论了一些技术细节:语法分析器是如何解释数据的,当文本周围有空格和回车时,如何处理文本,以及解析什么实体等等。然而使用 XML 时会产生一个语义的附加层。虽然这主要是理论上的讨论,但值得指出的是存在着涉及到比较 XML 的非技术性问题。

    这种不同的最好示例是属性对元素的典型争论。换句话说:文档中的数据是以元素存储还是以属性存储?如果两个文档有相同的数据,但存储的方式不同,那么这两个文档是相同的?还是不同的?看一下清单 10。

    清单 10. 使用属性而不是元素的 XML 文档

    <?xml version="1.0"?>
    <!DOCTYPE hockeyTeam SYSTEM "hockeyTeam.DTD">

    <hockeyTeam>
    <city value="Dallas"/>
    <state value="Texas"/>
    <mascot value="Stars"/>
    <arena name="Reunion Arena">
    <ice quality="poor" />
    <location city="Dallas" />
    </arena>
    <conference value="Western"/>
    <division value="Pacific"/>
    <nhlCopyright>&NHLCopyright;</nhlCopyright>
    </hockeyTeam>

    这与清单 1 中的数据是一样的,但这些数据是以属性表示而不是以元素表示。这两个文档在技术上相同吗?不,完全不同。然而,您可能争辩说,这两个文档中的数据是相同的。如果那是事实,那么您可能认为文档自身具有相同的含义。

    现在,为避免我把您弄糊涂,这纯粹是理论上的讨论,不存在任何 API 可将这两个文档解释为等价的。您必须自己决定是否需要花些功夫来编写处理这两个文档的代码,但应该意识到存在的这些差别,因为某一天您可能不得不处理它们。

    总结
    现在您应该充分理解了当两个文档“相同”时意味着什么。您知道为什么象 diff 这样简单的程序简单到不足以比较 XML 文档。我希望您可以利用这里显示的一些代码来区分 XML 文档中的比较点,使您能更容易地执行 XML 比较。

    从中寻找乐趣吧,网上见!


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    xml这门语言太好了,我们共同努力吧!!!!!

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2004/11/9 15:17:00
     
     GoogleAdSense双子座1983-6-17
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 DTD/XML Schema 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/21 23:46:55

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    62.500ms