2013-05-14 140 views
15

支持,我有兩個XML字符串比較兩個XML字符串中忽略元素順序

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

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

如何編寫一個測試,比較這兩個字符串,而忽略元素的順序?

我想測試儘可能短,沒有地方10行XML解析等。我正在尋找一個簡單的斷言或smt類似。

我有這個(這不工作)

Diff diff = XMLUnit.compareXML(expectedString, actualString); 
    XMLAssert.assertXMLEqual("meh", diff, true); 
+3

解析xml和在一組中添加值 – user1121883 2013-05-14 10:06:04

+0

任何只需在XMLUnit中添加幾行的方法? – 2013-05-14 10:06:46

+0

很明顯,你應該寫出你需要的案例。沒有現成的解決方案,因爲這是不尋常的要求。 – kan 2013-05-14 10:06:59

回答

7

我原來的答案已過時。如果我不得不再次構建它,我會使用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))); 
} 

如果有人仍然不想在這裏使用純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())); 
} 
+0

doc等於什麼? – 2013-05-14 10:23:35

+0

xml dom;我使用它加載它:DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); InputSource is = new InputSource(new StringReader(xml)); return builder.parse(is); – user1121883 2013-05-14 10:24:56

+0

這是好的,只是將設置更改爲列表,我沒有說它是一組 – 2013-05-14 10:28:15

0

OPTION 1
如果XML代碼很簡單,試試這個:

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


OPTION 2
如果XML更詳細,則使用XML分析器加載它呃並比較找到的參考節點的實際節點。

+0

@ option2:以自動方式執行它的任何方式? – 2013-05-14 10:13:39

17

XMLUnit會做你想做的,但你必須指定elementQualifier。沒有指定elementQualifier時,它只會比較同一位置的節點。

對於你想要一個ElementNameAndTextQualifer你的榜樣,這考慮了類似的一個節點如果存在該元素的名稱相匹配,並且它的文本價值,是這樣的:

Diff diff = new Diff(control, toTest); 
// we don't care about ordering 
diff.overrideElementQualifier(new ElementNameAndTextQualifier()); 
XMLAssert.assertXMLEqual(diff, true); 

你可以閱讀更多關於它:http://xmlunit.sourceforge.net/userguide/html/ar01s03.html#ElementQualifier

+0

在我的情況下,XMLUnit 1.6需要將'false'傳遞給第二個參數以便達到類似的比較而不是相同的。 – Vadzim 2017-06-29 17:13:39

0

Compare XML ignoring order of child elements

我今天晚上也有類似的需求,並不能找到適合我的東西REQ跨發佈uirements。

我的解決方法是對我想要區分的兩個XML文件進行排序,按元素名稱按字母順序排序。一旦他們都是一致的順序,我可以使用常規的可視化比較工具來區分兩個排序文件。

如果這種方法聽起來別人有用的,我與他們分享的Python腳本,我寫在http://dalelane.co.uk/blog/?p=3225

0

來進行排序正如如何比較更復雜的XML元素的例子匹配基於屬性的平等name。例如:

<request> 
    <param name="foo" style="" type="xs:int"/> 
    <param name="Cookie" path="cookie" style="header" type="xs:string" /> 
</request> 

<request> 
    <param name="Cookie" path="cookie" style="header" type="xs:string" /> 
    <param name="foo" style="query" type="xs:int"/> 
</request> 

有了以下自定義元素修飾詞:

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); 
14

對於XMLUnit測試2.0(我一直在尋找這一點)現在是做,通過使用DefaultNodeMatcher

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

希望這有助於這會有所幫助其他人googling ...

+0

是的,它幫助,它是一個完美的,純粹的解決方案。 – peterh 2016-10-27 17:16:33

+0

在2.4.0版本中,它是DiffBuilder.compare ...而不是Diffbuilder.compare ...。 – 2017-11-02 08:20:10

0

對我來說,我還需要添加方法:checkForSimilar()DiffBuilder。 沒有它,斷言是錯誤的說法,該節點的順序是不一樣的(在子列表中的位置是不一樣的)

我的代碼是:

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