Java 比较两个忽略元素顺序的 XML 字符串
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16540318/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Compare two XML strings ignoring element order
提问by Roay Spol
Suppose I have two xml strings
假设我有两个 xml 字符串
<test>
<elem>a</elem>
<elem>b</elem>
</test>
<test>
<elem>b</elem>
<elem>a</elem>
</test>
How to write a test that compares those two strings and ignores the element order?
如何编写一个测试来比较这两个字符串并忽略元素顺序?
I want the test to be as short as possible, no place for 10-line XML parsing etc. I'm looking for a simple assertion or something similar.
我希望测试尽可能短,没有地方进行 10 行 XML 解析等。我正在寻找一个简单的断言或类似的东西。
I have this (which doesn't work)
我有这个(这不起作用)
Diff diff = XMLUnit.compareXML(expectedString, actualString);
XMLAssert.assertXMLEqual("meh", diff, true);
采纳答案by user1121883
My original answer is outdated. If I would have to build it again i would use xmlunit 2 and xmlunit-matchers. Please note that for xml unit a different order is always 'similar' not equals.
我原来的答案已经过时了。如果我必须再次构建它,我将使用 xmlunit 2 和 xmlunit-matchers。请注意,对于 xml 单元,不同的顺序总是“相似”不等于。
@Test
public void testXmlUnit() {
String myControlXML = "<test><elem>a</elem><elem>b</elem></test>";
String expected = "<test><elem>b</elem><elem>a</elem></test>";
assertThat(myControlXML, isSimilarTo(expected)
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)));
//In case you wan't to ignore whitespaces add ignoreWhitespace().normalizeWhitespace()
assertThat(myControlXML, isSimilarTo(expected)
.ignoreWhitespace()
.normalizeWhitespace()
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)));
}
If somebody still want't to use a pure java implementation here it is. This implementation extracts the content from xml and compares the list ignoring order.
如果有人仍然不想在这里使用纯 Java 实现。此实现从 xml 中提取内容并比较列表忽略顺序。
public static Document loadXMLFromString(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(xml));
return builder.parse(is);
}
@Test
public void test() throws Exception {
Document doc = loadXMLFromString("<test>\n" +
" <elem>b</elem>\n" +
" <elem>a</elem>\n" +
"</test>");
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("//test//elem");
NodeList all = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
List<String> values = new ArrayList<>();
if (all != null && all.getLength() > 0) {
for (int i = 0; i < all.getLength(); i++) {
values.add(all.item(i).getTextContent());
}
}
Set<String> expected = new HashSet<>(Arrays.asList("a", "b"));
assertThat("List equality without order",
values, containsInAnyOrder(expected.toArray()));
}
回答by Stephan
OPTION 1
If the XML code is simple, try this:
选项 1
如果 XML 代码很简单,请尝试以下操作:
String testString = ...
assertTrue(testString.matches("(?m)^<test>(\s*<elem>(a|b)</elem>\s*){2}</test>$"));
OPTION 2
If the XML is more elaborate, load it with an XML parser and compare the actual nodes found with you reference nodes.
选项 2
如果 XML 更复杂,请使用 XML 解析器加载它并将找到的实际节点与您的参考节点进行比较。
回答by Steve Lancashire
XMLUnit will do what you want, but you have to specify the elementQualifier. With no elementQualifier specified it will only compare the nodes in the same position.
XMLUnit 将执行您想要的操作,但您必须指定 elementQualifier。如果没有指定 elementQualifier,它只会比较相同位置的节点。
For your example you want an ElementNameAndTextQualifer, this considers a node similar if one exists that matches the element name and it's text value, something like :
对于您的示例,您需要一个 ElementNameAndTextQualifer,如果存在与元素名称及其文本值匹配的节点,则它会考虑一个类似的节点,例如:
Diff diff = new Diff(control, toTest);
// we don't care about ordering
diff.overrideElementQualifier(new ElementNameAndTextQualifier());
XMLAssert.assertXMLEqual(diff, true);
You can read more about it here: http://xmlunit.sourceforge.net/userguide/html/ar01s03.html#ElementQualifier
您可以在此处阅读更多相关信息:http: //xmlunit.sourceforge.net/userguide/html/ar01s03.html#ElementQualifier
回答by dalelane
Cross-posting from Compare XML ignoring order of child elements
I had a similar need this evening, and couldn't find something that fit my requirements.
今晚我有类似的需求,但找不到符合我要求的东西。
My workaround was to sort the two XML files I wanted to diff, sorting alphabetically by the element name. Once they were both in a consistent order, I could diff the two sorted files using a regular visual diff tool.
我的解决方法是对我想要比较的两个 XML 文件进行排序,按元素名称的字母顺序排序。一旦它们的顺序一致,我就可以使用常规的可视化差异工具比较这两个排序的文件。
If this approach sounds useful to anyone else, I've shared the python script I wrote to do the sorting at http://dalelane.co.uk/blog/?p=3225
如果这种方法对其他人有用,我已经在http://dalelane.co.uk/blog/?p=3225 上分享了我编写的用于排序的 python 脚本
回答by Stepan Vavra
Just as an example of how to compare more complex xml elements matching based on equality of attribute name
. For instance:
仅作为如何比较基于属性相等的更复杂 xml 元素匹配的示例name
。例如:
<request>
<param name="foo" style="" type="xs:int"/>
<param name="Cookie" path="cookie" style="header" type="xs:string" />
</request>
vs.
对比
<request>
<param name="Cookie" path="cookie" style="header" type="xs:string" />
<param name="foo" style="query" type="xs:int"/>
</request>
With following custom element qualifier:
使用以下自定义元素限定符:
final Diff diff = XMLUnit.compareXML(controlXml, testXml);
diff.overrideElementQualifier(new ElementNameAndTextQualifier() {
@Override
public boolean qualifyForComparison(final Element control, final Element test) {
// this condition is copied from super.super class
if (!(control != null && test != null
&& equalsNamespace(control, test)
&& getNonNamespacedNodeName(control).equals(getNonNamespacedNodeName(test)))) {
return false;
}
// matching based on 'name' attribute
if (control.hasAttribute("name") && test.hasAttribute("name")) {
if (control.getAttribute("name").equals(test.getAttribute("name"))) {
return true;
}
}
return false;
}
});
XMLAssert.assertXMLEqual(diff, true);
回答by Akzidenzgrotesk
For xmlunit 2.0 (I was looking for this) it is now done, by using DefaultNodeMatcher
对于 xmlunit 2.0(我一直在寻找这个)它现在已经完成,通过使用DefaultNodeMatcher
Diff diff = Diffbuilder.compare(Input.fromFile(control))
.withTest(Input.fromFile(test))
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.build()
Hope this helps this helps other people googling...
希望这有助于帮助其他人使用谷歌搜索...
回答by pierre
For me, I also needed to add the method : checkForSimilar()
on the DiffBuilder
.
Without it, the assert was in error saying that the sequence of the nodes was not the same (the position in the child list was not the same)
对我来说,我还需要checkForSimilar()
在DiffBuilder
. 没有它,断言是错误的,说节点的顺序不一样(子列表中的位置不一样)
My code was :
我的代码是:
Diff diff = Diffbuilder.compare(Input.fromFile(control))
.withTest(Input.fromFile(test))
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.checkForSimilar()
.build()