Лучший способ сравнить 2 XML-документа на Java

Я пытаюсь написать автоматический тест приложения, которое в основном переводит пользовательский формат сообщения в сообщение XML и отправляет его на другой конец. У меня есть хороший набор пар сообщений ввода / вывода, поэтому все, что мне нужно сделать, это отправить входные сообщения и прослушать XML-сообщение, которое выйдет на другой конец.

Когда приходит время сравнить фактический результат с ожидаемым, я сталкиваюсь с некоторыми проблемами. Моя первая мысль заключалась в том, чтобы просто сравнить строки ожидаемых и фактических сообщений. Это не работает очень хорошо, потому что данные примеров, которые у нас есть, не всегда отформатированы последовательно, и часто бывают разные псевдонимы, используемые для пространства имен XML (а иногда пространства имен вообще не используются).

Я знаю, что могу проанализировать обе строки, а затем пройтись по каждому элементу и сравнить их самостоятельно, и это будет несложно сделать, но у меня такое чувство, что есть способ получше или библиотека, которую я мог бы использовать.

Итак, сводится к следующему:

Учитывая две строки Java, каждая из которых содержит допустимый XML, как бы вы определить, являются ли они семантически эквивалентными? Бонусные баллы, если у вас есть способ определить, в чем разница.


person Mike Deck    schedule 26.09.2008    source источник


Ответы (14)


Похоже на работу для XMLUnit

Пример:

public class SomeTest extends XMLTestCase {
  @Test
  public void test() {
    String xml1 = ...
    String xml2 = ...

    XMLUnit.setIgnoreWhitespace(true); // ignore whitespace differences

    // can also compare xml Documents, InputSources, Readers, Diffs
    assertXMLEqual(xml1, xml2);  // assertXMLEquals comes from XMLTestCase
  }
}
person Tom    schedule 26.09.2008
comment
Я знал, что должно быть что-то подобное. Не могу поверить, что Google не нашел его для меня. Спасибо. - person Mike Deck; 27.09.2008
comment
У меня были проблемы с XMLUNit в прошлом, он был очень дерганым с версиями XML API и не оказался надежным. Однако прошло некоторое время с тех пор, как я отказался от него для XOM, так что, возможно, с тех пор он улучшился. - person skaffman; 27.09.2008
comment
Для новичков в XMLUnit обратите внимание, что по умолчанию myDiff.similar () будет возвращать false, если контрольный и тестовый документы отличаются отступом / новой строкой. Я ожидал такого поведения от myDiff.identical (), а не от myDiff.similar (). Включить XMLUnit.setIgnoreWhitespace (true); в вашем методе setUp, чтобы изменить поведение для всех тестов в вашем тестовом классе, или использовать его в отдельном методе тестирования, чтобы изменить поведение только для этого теста. - person Stew; 07.12.2012
comment
@Stew спасибо за ваш комментарий, только начинаю с XMLUnit и я уверен, что столкнулся бы с этой проблемой. +1 - person Jay; 11.08.2014
comment
Если вы пытаетесь это сделать с XMLUnit 2 на github, версия 2 - это полная переработка, поэтому этот пример предназначен для XMLUnit 1 на SourceForge. Кроме того, на странице sourceforge указано, что XMLUnit для Java 1.x по-прежнему будет поддерживаться. - person Yngvar Kristiansen; 25.02.2016
comment
В этом примере Junit 3, который использует extends TestCase, действительно олдскульный. Было бы очень полезно использовать пример Junit 4. - person Yngvar Kristiansen; 25.02.2016
comment
метод assertXMLEqual из XMLAssert.java. - person user2818782; 16.01.2018
comment
@skaffman, по общему признанию, 10 лет спустя .... но xmlunit очень хорошо работает для меня в тестах JUnit 4. - person ; 12.04.2018

Следующее будет проверять, равны ли документы, используя стандартные библиотеки JDK.

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setCoalescing(true);
dbf.setIgnoringElementContentWhitespace(true);
dbf.setIgnoringComments(true);
DocumentBuilder db = dbf.newDocumentBuilder();

Document doc1 = db.parse(new File("file1.xml"));
doc1.normalizeDocument();

Document doc2 = db.parse(new File("file2.xml"));
doc2.normalizeDocument();

Assert.assertTrue(doc1.isEqualNode(doc2));

normalize () нужен для того, чтобы убедиться в отсутствии циклов (технически их не было бы)

Приведенный выше код потребует, чтобы пробелы были одинаковыми внутри элементов, потому что он сохраняет и оценивает его. Стандартный синтаксический анализатор XML, поставляемый с Java, не позволяет вам установить функцию для предоставления канонической версии или понимания xml:space, если это будет проблемой, вам может потребоваться замена синтаксического анализатора XML, например xerces, или использовать JDOM.

person Archimedes Trajano    schedule 18.11.2010
comment
Это отлично работает для XML без пространств имен или с нормализованными префиксами пространств имен. Я сомневаюсь, что это работает, если один XML - это ‹ns1: a xmlns: ns1 = ns /›, а другой - ‹ns2: a xmlns: ns2 = ns /› - person koppor; 06.01.2013
comment
dbf.setIgnoringElementContentWhitespace (true) не дает результата. Я ожидал, что имя ‹root› ‹/root› не равно ‹root› имени ‹/name› в этом решении (с двумя пробелами), но XMLUnit дает тот же результат в этом случае (JDK8) - person Miklos Krivan; 07.09.2016
comment
Для меня он не игнорирует разрывы строк, что является проблемой. - person Flyout91; 11.12.2017

Xom имеет служебную программу Canonicalizer, которая превращает ваши модели DOM в обычную форму, которую вы затем можете преобразовать в строки и сравнить. Таким образом, независимо от неправильных пробелов или порядка атрибутов, вы можете получать регулярные предсказуемые сравнения ваших документов.

Это особенно хорошо работает в IDE, в которых есть специальные визуальные компараторы String, например Eclipse. Вы получаете наглядное представление о семантических различиях между документами.

person skaffman    schedule 26.09.2008

Последняя версия XMLUnit может помочь в утверждении двух одинаковых XML. Также XMLUnit.setIgnoreWhitespace() и XMLUnit.setIgnoreAttributeOrder() могут быть необходимы для рассматриваемого дела.

См. Рабочий код простого примера использования модуля XML ниже.

import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Assert;

public class TestXml {

    public static void main(String[] args) throws Exception {
        String result = "<abc             attr=\"value1\"                title=\"something\">            </abc>";
        // will be ok
        assertXMLEquals("<abc attr=\"value1\" title=\"something\"></abc>", result);
    }

    public static void assertXMLEquals(String expectedXML, String actualXML) throws Exception {
        XMLUnit.setIgnoreWhitespace(true);
        XMLUnit.setIgnoreAttributeOrder(true);

        DetailedDiff diff = new DetailedDiff(XMLUnit.compareXML(expectedXML, actualXML));

        List<?> allDifferences = diff.getAllDifferences();
        Assert.assertEquals("Differences found: "+ diff.toString(), 0, allDifferences.size());
    }

}

Если вы используете Maven, добавьте это в свой pom.xml:

<dependency>
    <groupId>xmlunit</groupId>
    <artifactId>xmlunit</artifactId>
    <version>1.4</version>
</dependency>
person acdcjunior    schedule 09.05.2013
comment
Это идеально подходит для людей, которым нужно сравнивать статическим методом. - person Andy B; 02.07.2014
comment
Это прекрасный ответ. Спасибо .. Однако мне нужно игнорировать несуществующие узлы. Поскольку я не хочу видеть в выводе результата такой вывод: Ожидаемое присутствие дочернего узла нулевое, но было ...... Как я могу это сделать? С Уважением. @acdcjunior - person limonik; 20.12.2016
comment
XMLUnit.setIgnoreAttributeOrder (истина); не работает. Если некоторые узлы имеют другой порядок, сравнение не удастся. - person Bevor; 22.05.2017
comment
[ОБНОВЛЕНИЕ] это решение работает: stackoverflow.com/questions/ 33695041 / - person Bevor; 22.05.2017
comment
Вы ведь понимаете, что IgnoreAttributeOrder означает игнорировать порядок атрибутов и не игнорировать порядок узлов, верно? - person acdcjunior; 22.05.2017
comment
@acdcjunior Если узлы находятся в разных местах ... это сработает? - person Abhijit Bashetti; 18.09.2019
comment
@AbhijitBashetti Видимо только когда еще добавляешь .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName)). - person acdcjunior; 18.09.2019
comment
DiffBuilder.compare (bufferedReaderExistingFile) .withTest (bufferedReaderNewFile) .ignoreComments () .ignoreWhitespace () .withNodeMatcher (новый DefaultNodeMatcher (ElementSelectors.byName)) .checkForSimilar () .build (); - person Abhijit Bashetti; 18.09.2019
comment
@acdcjunior: у меня есть код выше, но он говорит, что файлы разные - person Abhijit Bashetti; 18.09.2019
comment
@AbhijitBashetti хм ... это плохо ... извини, у меня больше ничего нет ???? - person acdcjunior; 18.09.2019

Основываясь на ответе Тома, вот пример использования XMLUnit v2.

Он использует эти зависимости maven

    <dependency>
        <groupId>org.xmlunit</groupId>
        <artifactId>xmlunit-core</artifactId>
        <version>2.0.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.xmlunit</groupId>
        <artifactId>xmlunit-matchers</artifactId>
        <version>2.0.0</version>
        <scope>test</scope>
    </dependency>

..и вот тестовый код

import static org.junit.Assert.assertThat;
import static org.xmlunit.matchers.CompareMatcher.isIdenticalTo;
import org.xmlunit.builder.Input;
import org.xmlunit.input.WhitespaceStrippedSource;

public class SomeTest extends XMLTestCase {
    @Test
    public void test() {
        String result = "<root></root>";
        String expected = "<root>  </root>";

        // ignore whitespace differences
        // https://github.com/xmlunit/user-guide/wiki/Providing-Input-to-XMLUnit#whitespacestrippedsource
        assertThat(result, isIdenticalTo(new WhitespaceStrippedSource(Input.from(expected).build())));

        assertThat(result, isIdenticalTo(Input.from(expected).build())); // will fail due to whitespace differences
    }
}

Документация, описывающая это, выглядит так: https://github.com/xmlunit/xmlunit#comparing-two-documents

person Tom Saleeba    schedule 22.03.2016

Спасибо, я продлил это, попробуйте это ...

import java.io.ByteArrayInputStream;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

public class XmlDiff 
{
    private boolean nodeTypeDiff = true;
    private boolean nodeValueDiff = true;

    public boolean diff( String xml1, String xml2, List<String> diffs ) throws Exception
    {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        dbf.setCoalescing(true);
        dbf.setIgnoringElementContentWhitespace(true);
        dbf.setIgnoringComments(true);
        DocumentBuilder db = dbf.newDocumentBuilder();


        Document doc1 = db.parse(new ByteArrayInputStream(xml1.getBytes()));
        Document doc2 = db.parse(new ByteArrayInputStream(xml2.getBytes()));

        doc1.normalizeDocument();
        doc2.normalizeDocument();

        return diff( doc1, doc2, diffs );

    }

    /**
     * Diff 2 nodes and put the diffs in the list 
     */
    public boolean diff( Node node1, Node node2, List<String> diffs ) throws Exception
    {
        if( diffNodeExists( node1, node2, diffs ) )
        {
            return true;
        }

        if( nodeTypeDiff )
        {
            diffNodeType(node1, node2, diffs );
        }

        if( nodeValueDiff )
        {
            diffNodeValue(node1, node2, diffs );
        }


        System.out.println(node1.getNodeName() + "/" + node2.getNodeName());

        diffAttributes( node1, node2, diffs );
        diffNodes( node1, node2, diffs );

        return diffs.size() > 0;
    }

    /**
     * Diff the nodes
     */
    public boolean diffNodes( Node node1, Node node2, List<String> diffs ) throws Exception
    {
        //Sort by Name
        Map<String,Node> children1 = new LinkedHashMap<String,Node>();      
        for( Node child1 = node1.getFirstChild(); child1 != null; child1 = child1.getNextSibling() )
        {
            children1.put( child1.getNodeName(), child1 );
        }

        //Sort by Name
        Map<String,Node> children2 = new LinkedHashMap<String,Node>();      
        for( Node child2 = node2.getFirstChild(); child2!= null; child2 = child2.getNextSibling() )
        {
            children2.put( child2.getNodeName(), child2 );
        }

        //Diff all the children1
        for( Node child1 : children1.values() )
        {
            Node child2 = children2.remove( child1.getNodeName() );
            diff( child1, child2, diffs );
        }

        //Diff all the children2 left over
        for( Node child2 : children2.values() )
        {
            Node child1 = children1.get( child2.getNodeName() );
            diff( child1, child2, diffs );
        }

        return diffs.size() > 0;
    }


    /**
     * Diff the nodes
     */
    public boolean diffAttributes( Node node1, Node node2, List<String> diffs ) throws Exception
    {        
        //Sort by Name
        NamedNodeMap nodeMap1 = node1.getAttributes();
        Map<String,Node> attributes1 = new LinkedHashMap<String,Node>();        
        for( int index = 0; nodeMap1 != null && index < nodeMap1.getLength(); index++ )
        {
            attributes1.put( nodeMap1.item(index).getNodeName(), nodeMap1.item(index) );
        }

        //Sort by Name
        NamedNodeMap nodeMap2 = node2.getAttributes();
        Map<String,Node> attributes2 = new LinkedHashMap<String,Node>();        
        for( int index = 0; nodeMap2 != null && index < nodeMap2.getLength(); index++ )
        {
            attributes2.put( nodeMap2.item(index).getNodeName(), nodeMap2.item(index) );

        }

        //Diff all the attributes1
        for( Node attribute1 : attributes1.values() )
        {
            Node attribute2 = attributes2.remove( attribute1.getNodeName() );
            diff( attribute1, attribute2, diffs );
        }

        //Diff all the attributes2 left over
        for( Node attribute2 : attributes2.values() )
        {
            Node attribute1 = attributes1.get( attribute2.getNodeName() );
            diff( attribute1, attribute2, diffs );
        }

        return diffs.size() > 0;
    }
    /**
     * Check that the nodes exist
     */
    public boolean diffNodeExists( Node node1, Node node2, List<String> diffs ) throws Exception
    {
        if( node1 == null && node2 == null )
        {
            diffs.add( getPath(node2) + ":node " + node1 + "!=" + node2 + "\n" );
            return true;
        }

        if( node1 == null && node2 != null )
        {
            diffs.add( getPath(node2) + ":node " + node1 + "!=" + node2.getNodeName() );
            return true;
        }

        if( node1 != null && node2 == null )
        {
            diffs.add( getPath(node1) + ":node " + node1.getNodeName() + "!=" + node2 );
            return true;
        }

        return false;
    }

    /**
     * Diff the Node Type
     */
    public boolean diffNodeType( Node node1, Node node2, List<String> diffs ) throws Exception
    {       
        if( node1.getNodeType() != node2.getNodeType() ) 
        {
            diffs.add( getPath(node1) + ":type " + node1.getNodeType() + "!=" + node2.getNodeType() );
            return true;
        }

        return false;
    }

    /**
     * Diff the Node Value
     */
    public boolean diffNodeValue( Node node1, Node node2, List<String> diffs ) throws Exception
    {       
        if( node1.getNodeValue() == null && node2.getNodeValue() == null )
        {
            return false;
        }

        if( node1.getNodeValue() == null && node2.getNodeValue() != null )
        {
            diffs.add( getPath(node1) + ":type " + node1 + "!=" + node2.getNodeValue() );
            return true;
        }

        if( node1.getNodeValue() != null && node2.getNodeValue() == null )
        {
            diffs.add( getPath(node1) + ":type " + node1.getNodeValue() + "!=" + node2 );
            return true;
        }

        if( !node1.getNodeValue().equals( node2.getNodeValue() ) )
        {
            diffs.add( getPath(node1) + ":type " + node1.getNodeValue() + "!=" + node2.getNodeValue() );
            return true;
        }

        return false;
    }


    /**
     * Get the node path
     */
    public String getPath( Node node )
    {
        StringBuilder path = new StringBuilder();

        do
        {           
            path.insert(0, node.getNodeName() );
            path.insert( 0, "/" );
        }
        while( ( node = node.getParentNode() ) != null );

        return path.toString();
    }
}
person Javelin    schedule 28.04.2011
comment
Довольно поздно, но просто хотел отметить, что в этом фрагменте кода есть ошибка: в diffNodes () на node2 нет ссылки - второй цикл повторно использует node1 неправильно (я отредактировал код, чтобы исправить это). Кроме того, у него есть одно ограничение: из-за того, что дочерние карты связаны с ключами, этот diff не поддерживает случай, когда имена элементов не уникальны, то есть элементы, содержащие повторяющиеся дочерние элементы. - person aberrant80; 08.11.2012

AssertJ 1.4+ содержит определенные утверждения для сравнения содержимого XML:

String expectedXml = "<foo />";
String actualXml = "<bar />";
assertThat(actualXml).isXmlEqualTo(expectedXml);

Вот документация.

person Gian Marco    schedule 12.04.2016
comment
Однако тривиальная разница в префиксе пространства имен между двумя документами приводит к сбою AssertJ. AssertJ - отличный инструмент, но на самом деле он предназначен для XMLUnit. - person Alexander Vasiljev; 15.09.2020

Скаффман, кажется, дает хороший ответ.

другим способом, вероятно, является форматирование XML с помощью утилиты командной строки, такой как xmlstarlet (http://xmlstar.sourceforge.net/), а затем отформатируйте обе строки, а затем используйте любую утилиту (библиотеку) diff для сравнения полученных выходных файлов. Я не знаю, хорошее ли это решение, когда возникают проблемы с пространствами имен.

person anjanb    schedule 26.09.2008

Я использую Altova DiffDog, у которого есть параметры для структурного сравнения файлов XML ( игнорирование строковых данных).

Это означает, что (если установлен флажок «игнорировать текст»):

<foo a="xxx" b="xxx">xxx</foo>

а также

<foo b="yyy" a="yyy">yyy</foo> 

равны в том смысле, что имеют структурное равенство. Это удобно, если у вас есть файлы примеров, которые отличаются данными, но не структурой!

person Pimin Konstantin Kefaloukos    schedule 26.10.2010
comment
Единственный минус в том, что это не бесплатно (99 евро за профессиональную лицензию) с 30-дневной пробной версией. - person Pimin Konstantin Kefaloukos; 26.10.2010
comment
Я нашел только утилиту (altova.com/diffdog/diff-merge-tool .html); приятно иметь библиотеку. - person dma_k; 27.10.2010

Ниже код работает для меня

String xml1 = ...
String xml2 = ...
XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setIgnoreAttributeOrder(true);
XMLAssert.assertXMLEqual(actualxml, xmlInDb);
person arunkumar sambu    schedule 23.08.2017
comment
Любой контекст? Ссылка на библиотеку? - person Ben; 17.07.2019

Это позволит сравнить полные строковые XML-файлы (по ходу переформатируя их). Это упрощает работу с вашей IDE (IntelliJ, Eclipse), потому что вы просто нажимаете и визуально видите разницу в файлах XML.

import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.w3c.dom.Element;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.IOException;
import java.io.StringReader;

import static org.apache.xml.security.Init.init;
import static org.junit.Assert.assertEquals;

public class XmlUtils {
    static {
        init();
    }

    public static String toCanonicalXml(String xml) throws InvalidCanonicalizerException, ParserConfigurationException, SAXException, CanonicalizationException, IOException {
        Canonicalizer canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
        byte canonXmlBytes[] = canon.canonicalize(xml.getBytes());
        return new String(canonXmlBytes);
    }

    public static String prettyFormat(String input) throws TransformerException, ParserConfigurationException, IOException, SAXException, InstantiationException, IllegalAccessException, ClassNotFoundException {
        InputSource src = new InputSource(new StringReader(input));
        Element document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src).getDocumentElement();
        Boolean keepDeclaration = input.startsWith("<?xml");
        DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
        DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
        LSSerializer writer = impl.createLSSerializer();
        writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);
        writer.getDomConfig().setParameter("xml-declaration", keepDeclaration);
        return writer.writeToString(document);
    }

    public static void assertXMLEqual(String expected, String actual) throws ParserConfigurationException, IOException, SAXException, CanonicalizationException, InvalidCanonicalizerException, TransformerException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        String canonicalExpected = prettyFormat(toCanonicalXml(expected));
        String canonicalActual = prettyFormat(toCanonicalXml(actual));
        assertEquals(canonicalExpected, canonicalActual);
    }
}

Я предпочитаю это XmlUnit, потому что клиентский код (тестовый код) чище.

person Wojtek    schedule 14.10.2014
comment
Это отлично работает в двух тестах, которые я сделал сейчас, с тем же XML и с другим XML. С IntelliJ diff легко заметить различия в сравниваемых XML. - person Yngvar Kristiansen; 25.02.2016
comment
Кстати, если вы используете Maven, вам понадобится эта зависимость: ‹dependency› ‹groupId› org.apache.santuario ‹/groupId› ‹artifactId› xmlsec ‹/artifactId ‹›version› 2.0.6 ‹/version› / зависимость › - person Yngvar Kristiansen; 25.02.2016

Мне требовались те же функции, что и в основном вопросе. Поскольку мне не разрешалось использовать какие-либо сторонние библиотеки, я создал собственное решение на основе решения @Archimedes Trajano.

Вот мое решение.

import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.junit.Assert;
import org.w3c.dom.Document;

/**
 * Asserts for asserting XML strings.
 */
public final class AssertXml {

    private AssertXml() {
    }

    private static Pattern NAMESPACE_PATTERN = Pattern.compile("xmlns:(ns\\d+)=\"(.*?)\"");

    /**
     * Asserts that two XML are of identical content (namespace aliases are ignored).
     * 
     * @param expectedXml expected XML
     * @param actualXml actual XML
     * @throws Exception thrown if XML parsing fails
     */
    public static void assertEqualXmls(String expectedXml, String actualXml) throws Exception {
        // Find all namespace mappings
        Map<String, String> fullnamespace2newAlias = new HashMap<String, String>();
        generateNewAliasesForNamespacesFromXml(expectedXml, fullnamespace2newAlias);
        generateNewAliasesForNamespacesFromXml(actualXml, fullnamespace2newAlias);

        for (Entry<String, String> entry : fullnamespace2newAlias.entrySet()) {
            String newAlias = entry.getValue();
            String namespace = entry.getKey();
            Pattern nsReplacePattern = Pattern.compile("xmlns:(ns\\d+)=\"" + namespace + "\"");
            expectedXml = transletaNamespaceAliasesToNewAlias(expectedXml, newAlias, nsReplacePattern);
            actualXml = transletaNamespaceAliasesToNewAlias(actualXml, newAlias, nsReplacePattern);
        }

        // nomralize namespaces accoring to given mapping

        DocumentBuilder db = initDocumentParserFactory();

        Document expectedDocuemnt = db.parse(new ByteArrayInputStream(expectedXml.getBytes(Charset.forName("UTF-8"))));
        expectedDocuemnt.normalizeDocument();

        Document actualDocument = db.parse(new ByteArrayInputStream(actualXml.getBytes(Charset.forName("UTF-8"))));
        actualDocument.normalizeDocument();

        if (!expectedDocuemnt.isEqualNode(actualDocument)) {
            Assert.assertEquals(expectedXml, actualXml); //just to better visualize the diffeences i.e. in eclipse
        }
    }


    private static DocumentBuilder initDocumentParserFactory() throws ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(false);
        dbf.setCoalescing(true);
        dbf.setIgnoringElementContentWhitespace(true);
        dbf.setIgnoringComments(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        return db;
    }

    private static String transletaNamespaceAliasesToNewAlias(String xml, String newAlias, Pattern namespacePattern) {
        Matcher nsMatcherExp = namespacePattern.matcher(xml);
        if (nsMatcherExp.find()) {
            xml = xml.replaceAll(nsMatcherExp.group(1) + "[:]", newAlias + ":");
            xml = xml.replaceAll(nsMatcherExp.group(1) + "=", newAlias + "=");
        }
        return xml;
    }

    private static void generateNewAliasesForNamespacesFromXml(String xml, Map<String, String> fullnamespace2newAlias) {
        Matcher nsMatcher = NAMESPACE_PATTERN.matcher(xml);
        while (nsMatcher.find()) {
            if (!fullnamespace2newAlias.containsKey(nsMatcher.group(2))) {
                fullnamespace2newAlias.put(nsMatcher.group(2), "nsTr" + (fullnamespace2newAlias.size() + 1));
            }
        }
    }

}

Он сравнивает две строки XML и заботится о любых несоответствующих сопоставлениях пространств имен, переводя их в уникальные значения в обеих входных строках.

Возможна точная настройка, например, в случае перевода пространств имен. Но для моих требований просто выполняет свою работу.

person TouDick    schedule 15.04.2016

Использование JExamXML с приложением Java

    import com.a7soft.examxml.ExamXML;
    import com.a7soft.examxml.Options;

       .................

       // Reads two XML files into two strings
       String s1 = readFile("orders1.xml");
       String s2 = readFile("orders.xml");

       // Loads options saved in a property file
       Options.loadOptions("options");

       // Compares two Strings representing XML entities
       System.out.println( ExamXML.compareXMLString( s1, s2 ) );
person Sree    schedule 09.02.2013

Поскольку вы говорите «семантически эквивалентный», я предполагаю, что вы имеете в виду, что хотите сделать больше, чем просто проверить, что выходные данные xml равны (строка), и что вам нужно что-то вроде

‹Foo› кое-что здесь ‹/foo› ‹/code›

а также

‹Foo› кое-что здесь ‹/foo› ‹/code›

читать как эквивалент. В конечном итоге будет иметь значение, как вы определяете «семантически эквивалент» для любого объекта, из которого вы воссоздаете сообщение. Просто создайте этот объект из сообщений и используйте специальный метод equals (), чтобы определить, что вы ищете.

person Steve B.    schedule 27.09.2008
comment
Не ответ, а вопрос. - person Kartoch; 08.07.2011