Der beste Weg, um 2 XML-Dokumente in Java zu vergleichen

198

Ich versuche, einen automatisierten Test einer Anwendung zu schreiben, der im Grunde ein benutzerdefiniertes Nachrichtenformat in eine XML-Nachricht übersetzt und am anderen Ende sendet. Ich habe einen guten Satz von Eingabe / Ausgabe-Nachrichtenpaaren, daher muss ich nur die Eingabe-Nachrichten einschicken und darauf warten, dass die XML-Nachricht am anderen Ende herauskommt.

Wenn es darum geht, die tatsächliche Ausgabe mit der erwarteten Ausgabe zu vergleichen, treten einige Probleme auf. Mein erster Gedanke war nur, Zeichenfolgenvergleiche für die erwarteten und tatsächlichen Nachrichten durchzuführen. Dies funktioniert nicht sehr gut, da die Beispieldaten, die wir haben, nicht immer konsistent formatiert sind und häufig unterschiedliche Aliase für den XML-Namespace verwendet werden (und manchmal Namespaces überhaupt nicht verwendet werden).

Ich weiß, dass ich beide Zeichenfolgen analysieren und dann durch jedes Element gehen und sie selbst vergleichen kann. Dies wäre nicht allzu schwierig, aber ich habe das Gefühl, dass es einen besseren Weg oder eine Bibliothek gibt, die ich nutzen könnte.

Die Frage lautet also: Auf den Punkt gebracht:

Wie würden Sie bei zwei Java-Strings, die beide gültiges XML enthalten, feststellen, ob sie semantisch äquivalent sind? Bonuspunkte, wenn Sie die Unterschiede feststellen können.

Mike Deck
quelle

Antworten:

197

Klingt nach einem Job für XMLUnit

Beispiel:

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
  }
}
Tom
quelle
1
Ich hatte in der Vergangenheit Probleme mit XMLUNit, es war mit XML-API-Versionen sehr nervös und hat sich nicht als zuverlässig erwiesen. Es ist schon eine Weile her, dass ich es für XOM fallen gelassen habe, also ist es vielleicht seitdem ungestört.
Skaffman
63
Für Anfänger bis XMLUnit, beachten Sie, dass in der Standardeinstellung myDiff.similar () zurückkehren wird falsch , wenn die Kontroll- und Testunterlagen in Vertiefung / Zeilenumbrüche unterscheiden. Ich habe dieses Verhalten von myDiff.identical () und nicht von myDiff.similar () erwartet. Include XMLUnit.setIgnoreWhitespace (true); in Ihrer setUp-Methode, um das Verhalten für alle Tests in Ihrer Testklasse zu ändern, oder verwenden Sie es in einer einzelnen Testmethode, um das Verhalten nur für diesen Test zu ändern.
Eintopf
1
@Stew, danke für Ihren Kommentar, der gerade mit XMLUnit begonnen hat, und ich bin mir sicher, dass dieses Problem aufgetreten wäre. +1
Jay
2
Wenn Sie dies mit XMLUnit 2 auf github versuchen, ist die Version 2 vollständig neu geschrieben. Dieses Beispiel gilt also für XMLUnit 1 auf SourceForge. Außerdem heißt es auf der SourceForge-Seite "XMLUnit für Java 1.x wird weiterhin beibehalten".
Yngvar Kristiansen
1
Methode ist assertXMLEqual wie aus XMLAssert.java .
user2818782
36

Im Folgenden wird anhand von Standard-JDK-Bibliotheken überprüft, ob die Dokumente gleich sind.

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

Dokument doc1 = db.parse (neue Datei ("file1.xml"));
doc1.normalizeDocument ();

Dokument doc2 = db.parse (neue Datei ("file2.xml"));
doc2.normalizeDocument ();

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

normalize () ist da, um sicherzustellen, dass es keine Zyklen gibt (technisch würde es keine geben)

Für den obigen Code müssen die Leerzeichen innerhalb der Elemente jedoch identisch sein, da sie beibehalten und ausgewertet werden. Der mit Java xml:spacegelieferte Standard-XML-Parser ermöglicht es Ihnen nicht, eine Funktion für die Bereitstellung einer kanonischen Version festzulegen oder zu verstehen, ob dies ein Problem darstellt. Möglicherweise benötigen Sie einen XML-Ersatzparser wie xerces oder verwenden JDOM.

Archimedes Trajano
quelle
4
Dies funktioniert perfekt für XMLs ohne Namespaces oder mit "normalisierten" Namespace-Präfixen. Ich bezweifle, dass es funktioniert, wenn ein XML <ns1: a xmlns: ns1 = "ns" /> und das andere <ns2: a xmlns: ns2 = "ns" /> ist
koppor
dbf.setIgnoringElementContentWhitespace (true) hat nicht das Ergebnis Ich würde erwarten, dass der <root> -Name </ root> bei dieser Lösung (mit zwei Leerzeichen aufgefüllt) nicht mit <root> name </ name> übereinstimmt, aber XMLUnit liefert das gleiche Ergebnis in diesem Fall (JDK8)
Miklos Krivan
Für mich werden Zeilenumbrüche nicht ignoriert, was ein Problem ist.
Flyout91
setIgnoringElementContentWhitespace(false)
Archimedes Trajano
28

Xom verfügt über ein Canonicalizer-Dienstprogramm, das Ihre DOMs in eine reguläre Form umwandelt, die Sie dann anordnen und vergleichen können. Unabhängig von Leerzeichenunregelmäßigkeiten oder Attributreihenfolgen können Sie regelmäßige, vorhersehbare Vergleiche Ihrer Dokumente erhalten.

Dies funktioniert besonders gut in IDEs mit dedizierten visuellen String-Komparatoren wie Eclipse. Sie erhalten eine visuelle Darstellung der semantischen Unterschiede zwischen den Dokumenten.

Skaffman
quelle
21

Die neueste Version von XMLUnit kann dazu beitragen, dass zwei XML- Dateien gleich sind. Auch XMLUnit.setIgnoreWhitespace()und XMLUnit.setIgnoreAttributeOrder()kann für den fraglichen Fall notwendig sein.

Siehe Arbeitscode eines einfachen Beispiels für die Verwendung von XML-Einheiten weiter unten.

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

}

Wenn Sie Maven verwenden, fügen Sie Folgendes hinzu pom.xml:

<dependency>
    <groupId>xmlunit</groupId>
    <artifactId>xmlunit</artifactId>
    <version>1.4</version>
</dependency>
acdcjunior
quelle
Dies ist perfekt für Leute, die mit einer statischen Methode vergleichen müssen.
Andy B
Das ist die perfekte Antwort. Vielen Dank. Allerdings muss ich die Knoten ignorieren, die nicht vorhanden sind. Da ich in der Ergebnisausgabe keine solche Ausgabe sehen möchte: Erwartetes Vorhandensein des untergeordneten Knotens "null" aber war ...... Wie kann ich das machen? Grüße. @acdcjunior
limonik
1
XMLUnit.setIgnoreAttributeOrder (true); funktioniert nicht. Wenn einige Knoten eine andere Reihenfolge haben, schlägt der Vergleich fehl.
Bevor
[UPDATE] Diese Lösung funktioniert: stackoverflow.com/questions/33695041/…
Bevor
Sie erkennen, dass "IgnoreAttributeOrder" bedeutet, die Attributreihenfolge zu ignorieren und die Knotenreihenfolge nicht zu ignorieren, oder?
Acdcjunior
7

Danke, ich habe das erweitert, versuche das ...

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();
    }
}
Speer
quelle
3
Ziemlich spät, wollte aber nur beachten, dass dieser Code einen Fehler aufweist: In diffNodes () wird auf Knoten2 nicht verwiesen - die zweite Schleife verwendet Knoten1 falsch (ich habe den Code bearbeitet, um dies zu beheben). Außerdem gibt es eine Einschränkung: Aufgrund der Art und Weise, wie untergeordnete Zuordnungen verschlüsselt werden, unterstützt dieser Unterschied nicht den Fall, dass Elementnamen nicht eindeutig sind, dh Elemente, die wiederholbare untergeordnete Elemente enthalten.
aberrant80
7

Aufbauend auf Toms Antwort finden Sie hier ein Beispiel mit XMLUnit v2.

Es verwendet diese Maven-Abhängigkeiten

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

..und hier ist der Testcode

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

Die Dokumentation, in der dies beschrieben wird, lautet https://github.com/xmlunit/xmlunit#comparing-two-documents

Tom Saleeba
quelle
3

Skaffman scheint eine gute Antwort zu geben.

Eine andere Möglichkeit besteht wahrscheinlich darin, das XML mit einem Befehlszeilenprogramm wie xmlstarlet ( http://xmlstar.sourceforge.net/ ) zu formatieren und dann beide Zeichenfolgen zu formatieren und dann ein beliebiges diff-Dienstprogramm (Bibliothek) zu verwenden, um die resultierenden Ausgabedateien zu unterscheiden. Ich weiß nicht, ob dies eine gute Lösung ist, wenn Probleme mit Namespaces auftreten.

anjanb
quelle
3

AssertJ 1.4+ enthält spezielle Zusicherungen zum Vergleichen von XML-Inhalten:

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

Hier ist die Dokumentation

Gian Marco Gherardi
quelle
2

Ich verwende Altova DiffDog, das Optionen zum strukturellen Vergleichen von XML-Dateien bietet (ohne Berücksichtigung von Zeichenfolgendaten).

Dies bedeutet, dass (wenn Sie die Option "Text ignorieren" aktivieren):

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

und

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

sind gleich in dem Sinne, dass sie strukturelle Gleichheit haben. Dies ist praktisch, wenn Sie Beispieldateien haben, die sich in den Daten unterscheiden, aber nicht strukturiert sind!

Pimin Konstantin Kefaloukos
quelle
3
Das einzige Minus ist, dass es nicht kostenlos ist (99 € für eine Pro-Lizenz), mit 30-Tage-Testversion.
Pimin Konstantin Kefaloukos
2
Ich habe nur das Dienstprogramm gefunden ( altova.com/diffdog/diff-merge-tool.html ); schön, eine Bibliothek zu haben.
dma_k
1

Dadurch werden XMLs mit vollständigen Zeichenfolgen verglichen (unterwegs neu formatiert). Es macht es einfach, mit Ihrer IDE (IntelliJ, Eclipse) zu arbeiten, da Sie einfach klicken und den Unterschied in den XML-Dateien visuell sehen.

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

Ich bevorzuge dies gegenüber XmlUnit, da der Client-Code (Testcode) sauberer ist.

Wojtek
quelle
1
Dies funktioniert gut in zwei Tests, die ich jetzt durchgeführt habe, mit demselben XML und mit unterschiedlichem XML. Mit IntelliJ diff sind die Unterschiede im verglichenen XML leicht zu erkennen.
Yngvar Kristiansen
1
Übrigens benötigen Sie diese Abhängigkeit, wenn Sie Maven verwenden: <Abhängigkeit> <Gruppen-ID> org.apache.santuario </ Gruppen-ID> <ArtefaktId> xmlsec </ Artefakt-ID> <Version> 2.0.6 </ version> </ Abhängigkeit>
Yngvar Kristiansen
1

Der folgende Code funktioniert für mich

String xml1 = ...
String xml2 = ...
XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setIgnoreAttributeOrder(true);
XMLAssert.assertXMLEqual(actualxml, xmlInDb);
Arunkumar Sambu
quelle
1
Irgendein Kontext? Bibliotheksreferenz?
Ben
0

Verwenden von JExamXML mit einer Java-Anwendung

    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 ) );
Sreehari
quelle
0

Ich benötigte die gleiche Funktionalität wie in der Hauptfrage angefordert. Da ich keine Bibliotheken von Drittanbietern verwenden durfte, habe ich meine eigene Lösung basierend auf der @ Archimedes Trajano-Lösung erstellt.

Das Folgende ist meine Lösung.

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

}

Es vergleicht zwei XML-Zeichenfolgen und behebt alle nicht übereinstimmenden Namespace-Zuordnungen, indem sie in eindeutige Werte in beiden Eingabezeichenfolgen übersetzt werden.

Kann fein abgestimmt werden, dh bei Übersetzung von Namespaces. Aber für meine Anforderungen macht es einfach den Job.

TouDick
quelle
-2

Da Sie "semantisch äquivalent" sagen, meine ich, dass Sie mehr als nur buchstäblich überprüfen möchten, ob die XML-Ausgaben (Zeichenfolge) gleich sind und ob Sie so etwas möchten

<foo> ein paar Sachen hier </ foo> </ code>

und

<foo> ein paar Sachen hier </ foo> </ code>

Lesen Sie als gleichwertig. Letztendlich wird es wichtig sein, wie Sie "semantisch äquivalent" für jedes Objekt definieren, von dem Sie die Nachricht wiederherstellen. Erstellen Sie dieses Objekt einfach aus den Nachrichten und definieren Sie mit einem benutzerdefinierten equals (), wonach Sie suchen.

Steve B.
quelle
4
Keine Antwort, sondern eine Frage.
Kartoch