【问题标题】:How do I ignore certain elements when comparing XML?比较 XML 时如何忽略某些元素?
【发布时间】:2023-11-02 03:11:01
【问题描述】:

我有一个这样的 XML 消息:

<root>
  <elementA>something</elementA>
  <elementB>something else</elementB>
  <elementC>yet another thing</elementC>
</root>

我想将被测方法产生的这种类型的消息与预期的消息进行比较,但我不关心elementA。因此,我希望将上述消息视为等于:

<root>
  <elementA>something different</elementA>
  <elementB>something else</elementB>
  <elementC>yet another thing</elementC>
</root>

我正在使用最新版本的XMLUnit

我想答案涉及创建自定义DifferenceListener;如果有东西可以使用,我只是不想重新发明*。

欢迎提出使用 XMLUnit 以外的库的建议。

【问题讨论】:

    标签: java xml junit xmlunit


    【解决方案1】:

    让我们变得简单,忽略节点,无论您要忽略多少个节点的长度,或者如果您不知道要忽略多少个节点 ,请按照这些简单的步骤操作

    这只是一个带有两个 xml 作为字符串的 XMLdiff 方法

    public static void XMLdiff(String XML1, String XML2){
                   
     //nodes you wanted to ignore
            String[] ignoreNodes = {"nodeX", "nodeY", "nodeZ"};
          
    //testnode(node,ignoreNodes) is handled below for finding and ignoring multiple nodes
            final Diff documentDiff = DiffBuilder.compare(XML1)
                                      .withTest(XML2)
                                      .withNodeFilter(node->testnode(node,ignoreNodes))
                                      .build();
    }
    
    private static boolean testnode(Node node, String[] name) {
    
            for (int i = 0; i < name.length; i++) {
    
                if (node.getNodeName().toString().equals(name[i])) {
                    return false;
                }
                if (name.length == 0) {
                    break;
                }
            }
            return true;
        }
    

    从此链接添加依赖项或 jar https://www.xmlunit.org/

    【讨论】:

      【解决方案2】:

      您现在可以在 XMLUnit 2.6.0 中尝试${xmlunit.ignore}(添加依赖项 xmlunit-placeholders)。示例代码如下。

      Diff diff = DiffBuilder
          .compare(expectedXML)
          .withTest(actualXML)
          .withDifferenceEvaluator(new PlaceholderDifferenceEvaluator())
          .build();
      

      预期的 XML:

      <root>
        <elementA>${xmlunit.ignore}</elementA>
        <elementB>something else</elementB>
        <elementC>yet another thing</elementC>
      </root>
      

      实际的 XML:

      <root>
        <elementA>anything</elementA>
        <elementB>something else</elementB>
        <elementC>yet another thing</elementC>
      </root>
      

      请注意,目前 ${xmlunit.ignore} 仅支持文本节点和属性值忽略,从 unit tests 可以看出。

      【讨论】:

        【解决方案3】:

        自从回答了这个问题后,XMLUnit 的情况发生了很大变化。

        您现在可以在使用 DiffBuilder 时轻松忽略节​​点:

        final Diff documentDiff = DiffBuilder
                    .compare(expectedSource)
                    .withTest(actualSource)
                    .withNodeFilter(node -> !node.getNodeName().equals(someName))
                    .build();
        

        如果您随后调用documentDiff.hasDifferences() 添加到过滤器的节点将被忽略。

        【讨论】:

        • 看起来不错 - 但如何将 ElementNameAndAttributeQualifier 与 DiffBuilder 一起使用?允许不同顺序的 xml 元素?
        【解决方案4】:

        我最终实现了一个DifferenceListener,它采用节点名称列表(带有命名空间)来忽略以下文本差异:

        public class IgnoreNamedElementsDifferenceListener implements DifferenceListener {
            private Set<String> blackList = new HashSet<String>();
        
            public IgnoreNamedElementsDifferenceListener(String ... elementNames) {
                for (String name : elementNames) {
                    blackList.add(name);
                }
            }
        
            public int differenceFound(Difference difference) {
                if (difference.getId() == DifferenceConstants.TEXT_VALUE_ID) {
                    if (blackList.contains(difference.getControlNodeDetail().getNode().getParentNode().getNodeName())) {
                        return DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
                    }
                }
        
                return DifferenceListener.RETURN_ACCEPT_DIFFERENCE;
            }
        
            public void skippedComparison(Node node, Node node1) {
        
            }
        }
        

        【讨论】:

        • 我很惊讶它这么复杂 - 如果 xmlunit 没有内置的东西来忽略特定元素,这令人失望?
        • 这个解决方案现在已经很老了。显然,新的 xmlunit 编程模型使用 ComparisonListener,而不是 DifferenceListener。
        【解决方案5】:

        我会使用 XSLT 和 identity transform 过滤掉我想忽略的元素,然后比较结果。

        请参阅前面的XSL: how to copy a tree, but removing some nodes ? SO。

        【讨论】:

        • 这也是解决问题的有效方法。感谢您的建议。