Java 忽略元素顺序比较两个XML字符串

Java 忽略元素顺序比较两个XML字符串,java,unit-testing,junit,xmlunit,Java,Unit Testing,Junit,Xmlunit,假设我有两个xml字符串 <test> <elem>a</elem> <elem>b</elem> </test> <test> <elem>b</elem> <elem>a</elem> </test> 选项1 如果XML代码很简单,请尝试以下操作: String testString = ... assertTrue(test

假设我有两个xml字符串

<test>
  <elem>a</elem>
  <elem>b</elem>
</test>

<test>
  <elem>b</elem>
  <elem>a</elem>
</test>

选项1
如果XML代码很简单,请尝试以下操作:

 String testString = ...
 assertTrue(testString.matches("(?m)^<test>(\\s*<elem>(a|b)</elem>\\s*){2}</test>$"));
String testString=。。。
assertTrue(testString.matches(“(?m)^(\\s*(a | b)\\s*){2}$”);

选项2

如果XML更复杂,请使用XML解析器加载它,并将找到的实际节点与引用节点进行比较。

我的原始答案已过时。如果我必须再次构建它,我将使用XMLUnit2和XMLUnitMatchers。请注意,对于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)));
}  
@测试
公共无效testXmlUnit(){
字符串myControlXML=“ab”;
应为字符串=“ba”;
资产(myControlXML、IsMilarto(预期)
.withNodeMatcher(新的默认NodeMatcher(ElementSelectors.ByName和Text));
//如果不想忽略空白,请添加ignoreWhitespace().normalizeWhitespace()
资产(myControlXML、IsMilarto(预期)
.ignoreWhitespace()
.normalize空白()
.withNodeMatcher(新的默认NodeMatcher(ElementSelectors.ByName和Text));
}  
如果有人仍然不想在这里使用纯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()));
}
publicstaticdocumentloadxmlfromstring(stringxml)引发异常{
DocumentBuilderFactory工厂=DocumentBuilderFactory.newInstance();
DocumentBuilder=factory.newDocumentBuilder();
InputSource is=新的InputSource(新的StringReader(xml));
返回builder.parse(is);
}
@试验
public void test()引发异常{
Document doc=loadXMLFromString(“\n”+
“b\n”+
“a\n”+
"");
XPathFactory XPathFactory=XPathFactory.newInstance();
XPath=xPathfactory.newXPath();
XPathExpression expr=xpath.compile(“//test//elem”);
NodeList all=(NodeList)expr.evaluate(doc,XPathConstants.NODESET);
列表值=新的ArrayList();
if(all!=null&&all.getLength()>0){
for(int i=0;i
XMLUnit将执行您想要的操作,但您必须指定elementQualifier。如果没有指定elementQualifier,它将只比较相同位置的节点

例如,如果您想要一个ElementNameAndTextQualifer,那么如果存在一个与元素名称及其文本值匹配的节点,则会考虑类似的节点,例如:

Diff diff = new Diff(control, toTest);
// we don't care about ordering
diff.overrideElementQualifier(new ElementNameAndTextQualifier());
XMLAssert.assertXMLEqual(diff, true);
您可以在此处阅读更多信息:

交叉发布

今天晚上我也有类似的需求,找不到符合我要求的东西

我的解决方法是对我想要区分的两个XML文件进行排序,按元素名称的字母顺序进行排序。一旦它们的顺序一致,我就可以使用常规的VisualDiff工具来区分这两个已排序的文件


如果这种方法对其他任何人都有用的话,我已经分享了我在

中编写的用于排序的python脚本,作为一个示例,说明如何比较更复杂的xml元素,根据属性的相等性进行匹配。例如:


vs


使用以下自定义元素限定符:

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);
对于XMLUnit2.0(我一直在寻找),现在可以使用


希望这有助于其他人搜索…

对我来说,我还需要在
DiffBuilder
上添加方法:
checkForSimilar()
。 如果没有它,断言错误地指出节点的顺序不相同(子列表中的位置不相同)

我的代码是:

 Diff diff = Diffbuilder.compare(Input.fromFile(control))
   .withTest(Input.fromFile(test))
   .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
   .checkForSimilar()
   .build()

我不知道他们采用了什么版本的解决方案,但没有任何效果(或者至少很简单),所以这里是我为那些有同样痛苦的人提供的解决方案

另外,我讨厌人们错过静态方法的导入或FQN类名

@测试
void given2XMLS_是不同的元素_序列_和空白(){
字符串testXml=“3 false”;
应为字符串=“false 3”;
assertThat(testXml).和(预期)
.ignoreWhitespace()
.normalize空白()
.withNodeMatcher(新的默认NodeMatcher(ElementSelectors.ByName和Text))
.areSimilar();
}

解析xml并以setAny方式添加值在XMLUnit中只需几行就可以了?显然,您应该根据您的情况编写它。没有现成的解决方案,因为这是一个不寻常的要求。解析XML并将文本节点列表与其他列表进行比较。可能更容易获得xpath(XPathExpression)的所有值来构建该列表set@option2:有没有一种自动化的方法?xml dom;我使用:DocumentBuilderFactory=DocumentBuilderFactory.newInstance()加载它;DocumentBuilder=factory.newDocumentBuilder();InputSource is=新的InputSource(新的StringReader(xml));返回builder.parse(is);这没关系,只需将集合更改为list,我没有说这是一个集合您说忽略元素顺序:D+1:无论如何,加载XML然后在其上运行XPath在Java中肯定不是直接的…是的,它有帮助,而且是一个完美的纯解决方案。它是DiffBuilder。比较。。。而不是Diffbuilder。比较。。。在2.4.0版本中。在我的例子中,XMLUnit 1.6需要将
false
传递给第二个参数,以便实现类似的比较,而不是相同的比较。
Diff diff = Diffbuilder.compare(Input.fromFile(control))
   .withTest(Input.fromFile(test))
   .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
   .build()
 Diff diff = Diffbuilder.compare(Input.fromFile(control))
   .withTest(Input.fromFile(test))
   .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
   .checkForSimilar()
   .build()
    @Test
void given2XMLS_are_different_elements_sequence_with_whitespaces(){
    String testXml = "<struct><int>3</int>  <boolean>false</boolean> </struct>";
    String expected = "<struct><boolean>false</boolean><int>3</int></struct>";

    XmlAssert.assertThat(testXml).and(expected)
            .ignoreWhitespace()
            .normalizeWhitespace()
            .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
            .areSimilar();
}