XML Vergleich (VisualStudio C#)

TresPuntos

Cadet 4th Year
Registriert
Juni 2018
Beiträge
113
Hallo,

habe ein kleines Problem und zwar weiß ich nicht, wie ich 2 XML-Dateien nur an bestimmten Stellen vergleichen kann.
Der Aufbau der XML-Datei sieht wie unten dargestellt aus. <ShowInSystemInfo>true</ShowInSystemInfo>, dass ist die einzige Zeile, die sich zur anderen XML-Datei unterscheiden darf. Wie kann ich nur die Knoten zwischen ApplicationCommandLink vergleichen. <ShowInSystemInfo>true</ShowInSystemInfo> soll bei dem Vergleich ausgeschlossen werden, denn der Wert kann auch false sein.


XML:
<?xml version="1.0" encoding="utf-8"?>
<Settings Version="0.0.0.0">
  <Setting Key="Insert" Description="" LastChanged="Datum" ValueType="HHH" Version="0.0.0.0">
    <Value>
      <ApplicationCommandLink xmlns:xsi="XXX" xmlns:xsd="XYX">
        <Origin>ddd</Origin>
        <ShowInQuickLaunch>dddsa</ShowInQuickLaunch>
        <ShowInSystemInfo>true</ShowInSystemInfo>
        <DetailApplicationContext />
      </ApplicationCommandLink>
    </Value>
  </Setting>
  <Setting Key="InsertForCopy" Description="" LastChanged="Datum" ValueType="aa" Version="0.0.0.0">
    <Value>
      <ApplicationCommandLink xmlns:xsi="XXX" xmlns:xsd="XYX">
        <AuthorisationKey>ddd</AuthorisationKey>
        <ShowInQuickLaunch>dsa</ShowInQuickLaunch>
        <ShowInSystemInfo>true</ShowInSystemInfo>
        <DetailApplicationContext />
      </ApplicationCommandLink>
    </Value>
  </Setting>
 </Setting>
 
Das lässt sich z.B. mit Winmerge (kostenlos) oder BeyondCompare (recht günstig) veranstalten.

Edit:
Du willst das sicherlich in C# machen? Dann vergesse meinen Beitrag ;-)
 
Gentlem4n schrieb:
Das lässt sich z.B. mit Winmerge (kostenlos) oder BeyondCompare (recht günstig) veranstalten.
Ich muss das in meinem eigenen Tool realisieren für den normalen Vergleich habe ich bereits Tools.
Sry wenn die Beschreibung etwas ungenau war
 
Nur mal so als Ideen:

1. Dateien einlesen (als XDocument etc), die Elements mit <ShowInSystemInfo> alle rausschmeissen und dann 1:1 vergleichen
2. Ein Schema daraus basteln, wo sich nur der Wert für <ShowInSystemInfo> unterscheiden darf und dann dagegen validieren.
 
  • Gefällt mir
Reaktionen: TresPuntos
Einlesen ist relativ leicht gemacht. Da man sowas easy findet spar ich dir die Suche, sowas geht eigentlich locker wie folgt:

C#:
public static XmlDocument Load( String filePath, String fileName )
{
    XmlDocument result = null;
    String fileNameWithPath = Path.Combine( filePath, fileName );
    StreamReader file;
    try {
        file = File.OpenText( fileNameWithPath );
        String input = file.ReadToEnd();
        file.Close();

        XmlDocument doc = new XmlDocument();
        doc.LoadXml( input );
        result = doc;
    }
    catch { //eventuelle Fehlerbehandlung }
    
    return result;
}

Ansonsten ist der Ansatz recht gut, den @Tolotos gebracht hat. Man kann über das XmlDocument recht einfach über die Nodes (<Node> ... </Node>) der XML Struktur iterieren und sich da eine entsprechende Struktur daraus erstellen. Dann hast du quasi eine Liste aus Nodes, die wieder eigene Childnodes haben können, oder ein entsprechend für dich angepasstes Konstrukt. Da dann für den Vergleich die rausschmeißen, die du nicht vergleichen willst und der Rest sollte dann äquivalent aussehen.
Mit Schemata hab ich bisher noch nicht gearbeitet, aber klingt per se auch nach ner netten und effektiveren Art und Weise, würde mich da auch mal rein lesen.
Zum XmlDocument findest du hier ein paar Dokumentationen und passende Methoden zur Verwendung.
 
  • Gefällt mir
Reaktionen: TresPuntos
Wie kann ich die Knoten denn rausschmeißen?
Und eben nur die Childknoten vergleichen, da liegt nämlich mein großes Problem.
Der Tipp hört sich nämlich gut an nur finde ich dazu nichts :(
 
Naja du liest ja an einer Stelle von den XML Daten jede Node ein. Das geht idR. über deren Namen, in deinem Fall zum Beispiel "Origin" oder "ShowInQuickLaunch". Das einzige, was du an dieser Stelle hinzufügst wäre dann zum Beispiel ein if(node.Name.Equals("ShowInSystemInfo")) continue;, der den aktuellen Interationsschritt zum Einlesen überspringt.
 
  • Gefällt mir
Reaktionen: TresPuntos
Erstmal danke für die ganze Hilfe
Tolotos schrieb:
Hab gerade keine Zeit, aber mal ganz grob als Ansatz:
Der Ansatz hat mir gut geholfen dabei kam der Code raus. Bekomme dabei aber noch eine Fehlermeldung "Ausnahme ausgelöst: "System.ArgumentException" in mscorlib.dll" . Die Variable k gibt genau den richtigen XML-Code aus, aber danach kommt leider beim Löschen diese Fehlermeldung.

C#:
 public void Test()
        {
            //XmlTextReader erzeugen
            XmlTextReader xmlReader = null;
            try
            {
                xmlReader = new XmlTextReader(@"C:\Beispiel\Test.xml");
            }
            catch
            {
                MessageBox.Show("Fehler beim Einlesen '");
                return;
            }
            try
            {
                while (xmlReader.Read())
                {
                    if (xmlReader.NodeType == XmlNodeType.Element)
                    {
                        if (xmlReader.Name == "Value")
                        {

                            MessageBox.Show("drinnen");
                            String k = xmlReader.ReadInnerXml();
                            MessageBox.Show(k);

                            XDocument doc = XDocument.Load(k);
                            doc.Descendants("ShowInSystemInfo").Remove();
                        }
                        else
                        {

                        }
                    }
                }
            }
            catch
            {

            }           
        }
 
Zuletzt bearbeitet:
Du brauchst bei dir:

C#:
XDocument doc = XDocument.Parse(k);

Das hier funktioniert bei mir (ohne XmlReader):

C#:
        public static XDocument GetXDoc(String filePath, String fileName)
        {
            XDocument result = null;

            String fileNameWithPath = Path.Combine(filePath, fileName);
            FileStream fs = new FileStream(fileNameWithPath, FileMode.Open, FileAccess.Read);
            try
            {
                XDocument doc = XDocument.Load(fs);
                result = doc;
            }
            finally
            {
                if (fs!=null)
                    fs.Close();
            }
            return result;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                XDocument doc1 = GetXDoc(@"c:\Temp", "Test1.xml");
                doc1.Descendants("ShowInSystemInfo").Remove();
                string s = doc1.ToString();
                XDocument doc2 = GetXDoc(@"c:\Temp", "Test2.xml");
                doc2.Descendants("ShowInSystemInfo").Remove();
                bool bEqual = doc1.ToString(SaveOptions.None).Equals(doc2.ToString(SaveOptions.None));
            }
            catch
            {
            }
        }
 
  • Gefällt mir
Reaktionen: TresPuntos
Ja ist besser als mein Denkansatz :D
Habe nur noch die Zeile
doc1.Descendants("Setting").Attributes("LastChanged").Remove();
hinzugefügt und nun zeigt es True an. LastChanged ändert sich natürlich immer und darf nicht beachtet werden
Sry für die ganze Mühe, bin beim Thema XML nicht fit :D
 
Zuletzt bearbeitet:
Eventuell könntest du mir bei einem ähnlichen Problem helfen.
Es gibt einige XML-Dateien, die ähnlich wie oben aufgebaut sind. Dabei muss man auch einzelne Knoten ausblenden (das gleiche Prinzip wie oben, dafür hab ich ja die Lösung). Es soll wieder nach Unterschieden gesucht werden. XML-Datei A ist riesig und XML-Datei B hat weniger Zeilen. Die Zeilen, die es in B aber gibt, sollen gleich der Zeilen in A sein. Wenn Zum Beispiel aber B in einem Knoten einen anderen Wert oder einen völlig neuen Knoten hat soll es false zurückgeben. Zur Veranschaulichung, was ich wirklich meine das Beispiel unten -> hier sollte true zurückgegeben werden, weil B zwar weniger Zeilen hat, die es aber gibt sind alle in Beispiel A enthalten und es gibt keine Zeile in B, die es nicht in A gibt.

Beispiel A:
XML:
<?xml version="1.0" encoding="utf-8"?>
<Settings Version="0.0.0.0">
  <Setting Key="Insert" Description="" LastChanged="Datum" ValueType="HHH" Version="0.0.0.0">
    <Value>
      <ApplicationCommandLink xmlns:xsi="XXX" xmlns:xsd="XYX">
        <Origin>ddd</Origin>
        <ShowInQuickLaunch>dddsa</ShowInQuickLaunch>
        <ShowInSystemInfo>true</ShowInSystemInfo>
        <DetailApplicationContext />
      </ApplicationCommandLink>
    </Value>
  </Setting>
  <Setting Key="InsertForCopy" Description="" LastChanged="Datum" ValueType="aa" Version="0.0.0.0">
    <Value>
      <ApplicationCommandLink xmlns:xsi="XXX" xmlns:xsd="XYX">
        <AuthorisationKey>ddd</AuthorisationKey>
        <ShowInQuickLaunch>dsa</ShowInQuickLaunch>
        <ShowInSystemInfo>true</ShowInSystemInfo>
        <DetailApplicationContext />
      </ApplicationCommandLink>
    </Value>
  </Setting>
</Settings>

Beispiel B:
XML:
<?xml version="1.0" encoding="utf-8"?>
<Settings Version="0.0.0.0">
  <Setting Key="Insert" Description="" LastChanged="Datum" ValueType="HHH" Version="0.0.0.0">
    <Value>
      <ApplicationCommandLink xmlns:xsi="XXX" xmlns:xsd="XYX">
        <Origin>ddd</Origin>
        <ShowInQuickLaunch>dddsa</ShowInQuickLaunch>
        <ShowInSystemInfo>true</ShowInSystemInfo>
        <DetailApplicationContext />
      </ApplicationCommandLink>
    </Value>
  </Setting>
</Setting>
 
Dürfen zwischen den Zeilen von B in A andere Zeilen auftauchen? Oder muss der Block so komplett enthalten sein?
Letzteres wäre einfach, da könnte man mit dem Remove arbeiten und dann einfach auf enhalten prüfen (beim string Vergleich).
Daher nehme ich mal Ersteres an ... ;)
Ich nehme auch an, du möchtest in B erst unterhalb von <Settings> prüfen, oder?
 
Also es kann die ganze XML-Datei verglichen werden (hab den remove Befehl gelernt :D). Es können zwischen den Knoten andere auftauchen, die halt in A enthalten sind, aber in B eben nicht.
Also am Besten Knoten von B nehmen und schauen ob es die in A gibt.
 
Ohne Gewähr auf Allgemeingültigkeit scheint dies zu funktionieren.
Der Trick ist dabei, die einzelnen "Zeilen" aus einem XDocument in einer flachen Liste zu erhalten und ggf. zu clonen.
Geht evt. mit einem XmlReader einfacher.

Interessant:
XElement.EqualityComparer.Equals(xe1, xe2) != xe1.Equals(xe2)

C#:
        private void button4_Click(object sender, EventArgs e)
        {
            XDocument doc1 = GetXDoc(@"c:\Temp", "Test3.xml");
            string s = doc1.ToString();
            XDocument doc2 = GetXDoc(@"c:\Temp", "Test4.xml");
            string setting = "Setting";
            XElement xSetting2 = doc2.Descendants(setting).First(); // o.ä.
            XeFilter(xSetting2);
            XElement x1Root = doc1.Root;
            XeFilter(x1Root);
            if (XeContains(x1Root, xSetting2))
                MessageBox.Show("Found!");

        }

        private void XeFilter(XElement xe)
        {
            xe.Descendants("ShowInSystemInfo").Remove(); // remove unnecessary nodes
        }

        private bool XeContains(XElement x1Root, XElement xSetting2)
        {
            List<XElement> x1Elements = x1Root.Descendants().ToList();
            for (int k=0; k< x1Elements.Count; k++)
                x1Elements[k] = RemoveChildren(x1Elements[k]);
            int index = 0;
            foreach (XElement xe in xSetting2.Descendants())
            {
                XElement xe2=RemoveChildren(xe);
                while (index < x1Elements.Count() && !(XElement.EqualityComparer.Equals(xe2, x1Elements[index])))
                    index++;
                if (index >= x1Elements.Count())
                    return false;
                index++;     
            }
          
            return true;
        }

        private XElement RemoveChildren(XElement xe)
        {
            XElement xeClone = new XElement(xe);
            while (xeClone.HasElements)
                xeClone.Elements().First().Remove();
            return xeClone;
        }
 
Zuletzt bearbeitet: (fehlte was)
  • Gefällt mir
Reaktionen: TresPuntos
Klappt super.
Ich muss eine größere Anwendung schreiben und genau dort lag mein großes Problem in diesen beiden Gebiete im finden der XML-Dateien, die anderen Suchen haben nach längerem hin und her funktioniert. Nur eben diese beiden Probleme nicht.
Nochmal vielen Dank dafür, hast Wortwörtlich meinen Arsch gerettet :D
 
Hi,
nochmal eine kleine Störung nach längerer Zeit von mir :D
Tolotos schrieb:
Ohne Gewähr auf Allgemeingültigkeit scheint dies zu funktionieren.
Der Beitrag von oben den ich soeben zitiert habe, wie ist es möglich nach gleichem Schema die Dateien zu vergleichen, nur das die Reihenfolge ebenfalls keine Rolle spielt?
Der XML-Vergleich soll nämlich die Version und LastChange nicht beachten. Zudem soll XtraSerializer weiterhin von dem Vergleich ausgeschlossen werden und die Datei A kann mehr Zeilen als B haben, aber die Zeilen, die beide haben müssen exakt gleich sein.
Wie man sieht ist sollte B nach den Kriterien gleich A sein.
Beispiel A:
XML:
<?xml version="1.0" encoding="utf-8"?>
<Settings Version="0.0.0.0">
  <Setting Key="Insert" Description="" LastChanged="Datum" ValueType="HHH" Version="0.0.0.0">
    <Value>
      <ApplicationCommandLink xmlns:xsi="XXX" xmlns:xsd="XYX">
        <Origin>ddd</Origin>
        <ShowInQuickLaunch>dddsa</ShowInQuickLaunch>
        <ShowInSystemInfo>true</ShowInSystemInfo>
        <DetailApplicationContext />
      </ApplicationCommandLink>
    </Value>
  </Setting>
  <Setting Key="InsertForCopy" Description="" LastChanged="Datum" ValueType="aa" Version="0.0.0.0">
    <Value>
      <ApplicationCommandLink xmlns:xsi="XXX" xmlns:xsd="XYX">
        <AuthorisationKey>ddd</AuthorisationKey>
        <ShowInQuickLaunch>dsa</ShowInQuickLaunch>
        <ShowInSystemInfo>true</ShowInSystemInfo>
        <DetailApplicationContext />
      </ApplicationCommandLink>
    </Value>
  </Setting>
</Settings>

Beispiel B:
XML:
<?xml version="1.0" encoding="utf-8"?>
<Settings Version="0.0.0.0">
  <Setting Key="Insert" Description="" LastChanged="Datum" ValueType="HHH" Version="0.0.0.0">
    <Value>
      <ApplicationCommandLink xmlns:xsi="XXX" xmlns:xsd="XYX">
        <ShowInSystemInfo>true</ShowInSystemInfo>
        <ShowInQuickLaunch>dddsa</ShowInQuickLaunch>
        <Origin>ddd</Origin>
        <DetailApplicationContext />
      </ApplicationCommandLink>
    </Value>
  </Setting>
</Setting>
 
Zurück
Oben