【问题标题】:Doc4j: Compare two documents fails due to different Element-typesDoc4j:由于元素类型不同,比较两个文档失败
【发布时间】:2017-08-09 17:25:00
【问题描述】:

我尝试为我编写的 Docx4J 生成器编写一些 JUnit 测试。 我想将生成器的输出节点与我想从字符串加载的预期节点进行比较。

所以,我像这样创建我的“实际”节点(生成器输出):

Node xmlNodeActual = XmlUtils.marshaltoW3CDomDocument(actual).getDocumentElement();

“实际”是我的生成器创建的对象。

对于我的“预期”节点,我编写了以下代码:

Document doc = docBuilder.parse(new InputSource(new ByteArrayInputStream(strXmlNode.getBytes("utf-8")))):
Node xmlNodeExpected = doc.getDocumentElement();

strXmlNode 是一个包含预期 xml 的字符串。 尽管从视觉差异中我可以看出我的两个节点是相等的,但调用以下结果会产生“假”:

xmlNodeActual.isEqualNode(xmlNodeExpected)

我怀疑原因是两个节点的运行时类型不同:

  • xmlNodeActual: org.apache.xerces.dom.DeferredElementImpl
  • xmlNodeExpected: org.apache.xerces.dom.ElementNSImpl

我喜欢我的测试设计,因为它可以让我为大型生成器快速编写大量测试用例。但是,我看不到将这种方法与“isEqualNode”结合使用的方法。 我是否必须编写自己的比较器,或者是否有我不知道的方法来确保节点的类型相同?

【问题讨论】:

    标签: java xml jaxb docx4j javax


    【解决方案1】:

    使用这样的方法的一个问题是它只给出一个布尔答案,它没有告诉你两个节点之间的实际差异是什么。另一个问题是你不能告诉它你认为重要的差异:例如(据我所知)冗余命名空间声明被这种特定方法认为是重要的。空白通常是有问题的。我在使用 XPath deep-equal() 方法时遇到了同样的问题,因此编写了 saxon:deep-equal 变体。但我现在更喜欢使用一组 XPath 断言来测试预期结果。 W3C XSLT 测试套件将这种技术与如下测试断言一起使用:

    <result>
         <all-of>
            <assert>/root/p[1]/text()[1] = 'Tekst '</assert>
            <assert>/root/p[1]/text()[2] = ' etc..'</assert>
            <assert>/root/p[2]/text()[1] = 'Tekst '</assert>
            <assert>/root/p[2]/text()[2] = ' etc..'</assert>
         </all-of>
      </result>
    

    我曾经有一个小工具可以从 XML 文档生成这样的断言列表,但我现在倾向于手动执行它们。最大的好处是,如果出现问题,诊断程序会告诉您哪个断言失败。

    【讨论】:

    • 感谢迈克尔,我知道我的方法的局限性。但考虑到我正在处理的技术和非功能限制,这将是一个很好的折衷方案。我比较的节点很小,但很多。在我的设置中,冗余命名空间依赖项和空格都不是问题,但感谢您指出。我以前为与 XML 不同的语言编写过类似 saxon:deep-equal 的东西,但我的问题不是关于我的方法,而是是否有一种简单的方法来控制解析产生的节点类型。
    • 规范没有提示基于第二个节点树的实现类允许比较失败,但当然实现中可能存在错误。就我个人而言,我认为您忽略的两棵树之间更有可能存在细微差别。可能值得下载 Saxon 并查看 fn:deep-equal()(或 saxon:deep-equal())的内容。
    • 我想我必须不同意这里:查看doc。其内容为:两个节点相等当且仅当满足以下条件: 两个节点属于同一类型。 ...
    • 我对“类型”的阅读有“getNodeType()”,例如两个属性或两个 cmets。我不认为这意味着它们必须具有相同的实现类,因为这通常超出了程序员的控制范围。
    【解决方案2】:

    【讨论】:

    • 嗨@JasonPlutext。感谢您的提示。这些,再加上一夜好眠,让我找到了我一直在寻找的务实解决方案(很快就会用我现在得到的结果更新我的问题)。一般来说,我认为 xmlunit 是正确的方法。附带说明:我的声誉还不够高,无法显示我对这个答案的支持。
    【解决方案3】:

    请注意,@Michael Kay 和 @JasonPlutext 提供了关于如何测试一般 XML 输出的有趣且更好的替代方案,您可能需要考虑这些替代方案。

    至于我的具体问题和问题,即简单地将两个 XML 节点与“isEqualNode”进行比较,一个来自输入字符串,一个来自数据转换,我必须执行以下操作:而不是解析字符串,我可以通过 InputStream 解组它,从而获得所需的节点类型。

    // creating the "actual" node I want to test (nothing changed here)
    Node xmlNodeActual = XmlUtils.marshaltoW3CDomDocument(actual).getDocumentElement();
    
    //...
    
    // Instead of parsing the string, just unmarshal and marshal it once
    Object expected = XmlUtils.unmarshal(new ByteArrayInputStream(strXmlNode.getBytes("utf-8")));
    Node xmlNodeExpected = XmlUtils.marshaltoW3CDomDocument(expected).getDocumentElement();
    if(!xmlNodeActual.isEqualNode(xmlNodeExpected)) {
    // ...
    }
    

    这会产生相同的节点类型并按预期适用于我的设置。不过,正如 Michael Kay 所指出的那样,这种比较两个 XML 树的方法存在一些缺陷,因此不要将其视为最佳实践,而应求助于一般 XML 比较的另一个答案。

    【讨论】:

      猜你喜欢
      • 2014-03-05
      • 2016-09-30
      • 2021-03-04
      • 1970-01-01
      • 2019-03-08
      • 2011-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多