2013-05-14 9 views
15

Wsparcie Mam dwa ciągi xmlPorównaj dwa ciągi XML ignorując kolejność występowania elementów

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

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

Jak napisać test, który porównuje te dwa ciągi i ignoruje kolejność elementów?

Chcę, aby test był możliwie jak najkrótszy, nie było miejsca na 10-wierszowe przetwarzanie XML itp. Szukam prostego asercji lub smt podobnego.

mam to (co robi praca)

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

analizowania XML i dodać wartości w zbiorze – user1121883

+0

Jakikolwiek sposób zrobić to w zaledwie kilku liniach w XMLUnit? –

+0

Oczywiście wystarczy napisać tak, jak potrzebujesz w swojej sprawie. Nie ma gotowego rozwiązania, ponieważ jest to nietypowy wymóg. – kan

Odpowiedz

7

Moja oryginalna odpowiedź jest nieaktualna. Gdybym musiał go ponownie zbudować, użyłbym xmlunit 2 i xmlunit-matchers. Należy pamiętać, że w przypadku jednostki xml inna kolejność jest zawsze "podobna", a nie równa się.

@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))); 
} 

Jeśli ktoś nadal nie chce używać czystej implementacji java, to jest. Ta implementacja wyodrębnia zawartość z xml i porównuje kolejność ignorowania listy.

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

co to jest dokumentacja równa się? –

+0

xml dom; ładuję go za pomocą: DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); InputSource is = new InputSource (new StringReader (xml)); return builder.parse (is); – user1121883

+0

to jest ok, po prostu zmień zestaw do listy, nie powiedziałem, że to zestaw –

0

WARIANT 1
Jeśli kod XML jest proste, spróbuj tego:

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


opcja 2
Jeśli XML jest bardziej rozbudowany, załaduj go z parsami XML er i porównaj rzeczywiste węzły znalezione z referencyjnymi węzłami.

+0

@ option2: jakikolwiek sposób zrobić to w sposób zautomatyzowany? –

17

XMLUnit zrobi, co chcesz, ale musisz określić elementQualifier. Bez elementu elementQualifier będzie porównywać tylko te węzły w tej samej pozycji.

Dla przykładu chcesz mieć ElementNameAndTextQualifer, to uważa węzeł podobny, jeśli taki istnieje, że jest zgodna z nazwą elementu oraz jego wartość tekst, coś jak:

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

Możesz przeczytać więcej na ten temat tutaj: http://xmlunit.sourceforge.net/userguide/html/ar01s03.html#ElementQualifier

+0

W moim przypadku XMLUnit 1.6 wymaga przekazania 'false' do drugiego argumentu w celu osiągnięcia podobne porównanie zamiast identycznego. – Vadzim

0

trasy wpis z Compare XML ignoring order of child elements

miałem podobną potrzebę tego wieczoru, i nie mógł znaleźć coś, co pasuje do mojej req uirements.

Moim obejściem było posortowanie dwóch plików XML, które chciałem zmienić, sortując alfabetycznie według nazwy elementu. Gdy obie były w spójnej kolejności, mogłem rozróżnić dwa posortowane pliki za pomocą zwykłego narzędzia wizualnego porównania.

Jeśli takie podejście brzmi przydatne do nikogo innego, ja podzielił skrypt Pythona I napisał do wykonania sortowania w http://dalelane.co.uk/blog/?p=3225

0

Tylko jako przykład, w jaki sposób porównać bardziej złożonych elementów XML dopasowania na podstawie równości atrybutu name. Na przykład:

<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> 

Z poniższego niestandardowych elementów kwalifikator:

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

Dla xmlunit 2.0 (szukałem tego) jest teraz zrobione, za pomocą DefaultNodeMatcher

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

nadzieję, że pomoże to pomaga inni ludzie google ...

+0

Tak, pomógł, i jest to doskonałe, czyste rozwiązanie. – peterh

+0

To DiffBuilder.compare ... zamiast Diffbuilder.compare ... w wersji 2.4.0. –

0

Dla mnie potrzebowałem również dodać metodę: checkForSimilar() na DiffBuilder. Bez niego dochodzić był w błędzie mówiąc, że sekwencja węzłów nie był taki sam (pozycja na liście dziecko było nie takie same)

Mój kod to:

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