比较 XMLUnit 2 中的子节点 - 应为子节点 'node2' 但为 'null'
Compare subnodes in XMLUnit 2 - Expected child 'node2' but was 'null'
我需要使用 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>
实际上,node1
和node2
是顺序改变的。测试结果为:
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 按名称匹配元素,没有附加条件。在您的情况下,这意味着 child
元素按顺序进行比较。您显然需要一种不同的策略,该策略在为任何给定的 child
.[= 决定哪个 select 节点作为 "partner" 时查看节点的第一个子节点。 22=]
一个选项是
new DefaultNodeMatcher(byXPath("./*[1]", byName), byName))
这将通过尝试匹配节点的第一个子节点来首先匹配节点,并且 - 如果未能提出任何候选者 - 使用元素本身的名称。
根据您真实文档的结构,这可能太天真了,您需要使用条件元素select或
new DefaultNodeMatcher(selectorForElementNamed("child", byXPath("./*[1]", byName)), byName))
注意:以上代码不适用于 XMLUnit 2.0.0-alpha-02,因为 issue #39 - 您必须使用 XPath "./child/*[1]"
代替。 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 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>
实际上,node1
和node2
是顺序改变的。测试结果为:
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 按名称匹配元素,没有附加条件。在您的情况下,这意味着 child
元素按顺序进行比较。您显然需要一种不同的策略,该策略在为任何给定的 child
.[= 决定哪个 select 节点作为 "partner" 时查看节点的第一个子节点。 22=]
一个选项是
new DefaultNodeMatcher(byXPath("./*[1]", byName), byName))
这将通过尝试匹配节点的第一个子节点来首先匹配节点,并且 - 如果未能提出任何候选者 - 使用元素本身的名称。
根据您真实文档的结构,这可能太天真了,您需要使用条件元素select或
new DefaultNodeMatcher(selectorForElementNamed("child", byXPath("./*[1]", byName)), byName))
注意:以上代码不适用于 XMLUnit 2.0.0-alpha-02,因为 issue #39 - 您必须使用 XPath "./child/*[1]"
代替。 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))