2013-05-14 2 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

+0

Любой способ сделать это всего за пару строк в XMLUnit? –

+0

Очевидно, что вы просто напишите, как вам нужно для вашего дела. Нет готового решения, потому что это необычное требование. – kan

ответ

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, равный? –

+0

xml dom; я загружаю его, используя: DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); InputSource is = new InputSource (новый StringReader (xml)); return builder.parse (is); – user1121883

+0

это нормально, просто измените набор на список, я не сказал, что это набор –

0

ВАРИАНТ 1
Если код XML прост, попробуйте следующее:

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


ВАРИАНТ 2
Если XML более сложный, загрузите его с помощью парсов XML er и сравнить фактические узлы, найденные с вами опорными узлами.

+0

@ option2: любой способ сделать это автоматическим способом? –

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

0

кросс-постинг из Compare XML ignoring order of child elements

этот вечер у меня была подобная необходимость, и не мог найти что-то, что соответствует моим REQ uirements.

Моим обходным путем было сортировать два файла XML, которые я хотел разделить, сортируя по алфавиту по имени элемента. Как только они были в одинаковом порядке, я мог бы разбить два отсортированных файла, используя обычный инструмент визуального разграничения.

Если этот подход кажется полезным для кого-то еще, я разделил питон скрипт, который я написал, чтобы сделать сортировку в 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> 

vs.

<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

+0

Это DiffBuilder.compare ... вместо Diffbuilder.compare ... в версии 2.4.0. –

0

Для меня мне также необходимо было добавить метод: checkForSimilar() на DiffBuilder. Без этого, утверждают было ошибкой, что последовательность узлов не была одинаковой (позиция в списке ребенка был не то же самое)

Мой код был:

Diff diff = Diffbuilder.compare(Input.fromFile(control)) 
    .withTest(Input.fromFile(test)) 
    .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)) 
    .checkForSimilar() 
    .build() 
Смежные вопросы