【问题标题】:Compare subnodes in XMLUnit 2 - Expected child 'node2' but was 'null'比较 XMLUnit 2 中的子节点 - 预期子节点 'node2' 但为 'null'
【发布时间】:2015-11-28 19:24:59
【问题描述】:

我需要使用 XMLUnit 2 比较 XML 文件,其中 xml 子节点的顺序可能不同。由于使用了底层库,我无法影响子节点的顺序。

为了比较,我正在使用:

    <dependency>
        <groupId>org.xmlunit</groupId>
        <artifactId>xmlunit-core</artifactId>
        <version>2.0.0-alpha-02</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.xmlunit</groupId>
        <artifactId>xmlunit-matchers</artifactId>
        <version>2.0.0-alpha-02</version>
        <scope>test</scope>
    </dependency>

问题归结为这个 JUnit 测试:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.xmlunit.builder.Input.fromString;
import static org.xmlunit.diff.ElementSelectors.byName;
import static org.xmlunit.matchers.CompareMatcher.isSimilarTo;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.xmlunit.diff.DefaultNodeMatcher;

import java.io.IOException;

public class XmlTest {

    @Test
    public void test() throws Exception {
        String t1 = fromClassPath("t1.xml");
        String t2 = fromClassPath("t2.xml");
        assertThat(fromString(t1), isSimilarTo(fromString(t2)).withNodeMatcher(new DefaultNodeMatcher(byName)));
    }

    private static String fromClassPath(String fileName) throws IOException {
        return IOUtils.toString(new ClassPathResource(fileName).getInputStream());
    }
}

其中t1.xml 包含:

<root>
    <child>
        <node1/>
    </child>
    <child>
        <node2/>
    </child>
</root>

t2.xml 包含:

<root>
    <child>
        <node2/>
    </child>
    <child>
        <node1/>                                                                                                                        
    </child>
</root>

其实node1node2是按顺序变化的。测试结果是:

java.lang.AssertionError: 
Expected: Expected child 'node2' but was 'null' - comparing <node2...> at /root[1]/child[1]/node2[1] to <NULL>:
<node2/>
     but: result was: 
<NULL>  

我想知道是否可以将此类 xml 文件与XMLUnit 2 进行比较。 任何形式的帮助表示赞赏。

【问题讨论】:

    标签: xmlunit xmlunit-2


    【解决方案1】:

    您告诉 XMLUnit 按名称匹配元素,无需附加条件。在您的情况下,这意味着按顺序比较 child 元素。您显然需要一个不同的策略,在决定选择哪个child 节点作为任何给定child 的“伙伴”时查看节点的第一个子节点。

    一种选择是

        new DefaultNodeMatcher(byXPath("./*[1]", byName), byName))
    

    这将首先通过尝试匹配节点的第一个子节点来匹配节点,并且 - 如果无法找到任何候选者 - 使用元素本身的名称。

    根据你真实文档的结构,这可能太幼稚了,你需要使用条件元素选择器

        new DefaultNodeMatcher(selectorForElementNamed("child", byXPath("./*[1]", byName)), byName))
    

    注意:以上代码不适用于 XMLUnit 2.0.0-alpha-02,因为 issue #39 - 您必须改用 "./child/*[1]" 的 XPath。 byXPath ElementSelector 之后已经修复。

    如果你不想使用 XPath,你可以自己编码一个ElementSelector

    public static class FirstChildElementNameSelector implements ElementSelector {
        @Override
        public boolean canBeCompared(Element controlElement,
                                     Element testElement) {
            return byName.canBeCompared(firstChildElement(controlElement),
                                        firstChildElement(testElement));
        }
    
        private Element firstChildElement(Element e) {
            NodeList nl = e.getChildNodes();
            int len = nl.getLength();
            for (int i = 0; i < len; i++) {
                if (nl.item(i) instanceof Element) {
                    return (Element) nl.item(i);
                }
            }
            return null;
        }
    }
    

    并像使用它

        new DefaultNodeMatcher(selectorForElementNamed("child", new FirstChildElementNameSelector()), byName))
    

    【讨论】:

    • 感谢您的回答。不幸的是,它也不起作用。我仍然遇到同样的失败。我调试到 XMLUnit 以查看发生了什么。我为node2 尝试了不同的节点匹配器or(selectorForElementNamed("node2", byXPath("./../*/node2", byName)), byName),反之亦然为node1,但仍然没有成功。
    • 你是正确的@ksokol 有两个原因。 byXPath 无法按照我对github.com/xmlunit/xmlunit/issues/39 的预期方式工作,而or 不是正确的选择。 github.com/xmlunit/xmlunit/commit/… 包含两个通过测试,标记为 02 的一个将不适用于 aplha-03 或 2.0.0(无论接下来发生什么),因为我打算使原始 XPath 表达式工作。修复 XPath 问题后,我将编辑答案。
    • 感谢您详尽的回答。我通过 XMLUnit 进一步调试,并有了自定义 NodeMatcher 的想法。您编辑的答案引导我找到正确的解决方案。目前我将使用FirstChildElementNameSelector。一旦 alpha-03 在 Maven 中心可用,我将尝试 byXPath ElementSelector。谢谢!
    • @ksokol 我今天发布了 XMLUnit 2.0.0-alpha-03。
    • @/stefan-bodewig 谢谢。它现在按预期工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-28
    • 2017-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多