Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   XML (https://www.delphipraxis.net/46-xml/)
-   -   C# Mit XPath Knoten auswählen liefert alle Knotenwerte (https://www.delphipraxis.net/113533-mit-xpath-knoten-auswaehlen-liefert-alle-knotenwerte.html)

Luckie 9. Mai 2008 11:34


Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Ich habe einen etwas komischen Effekt, den ich nicht verstehe.

Hier erst mal die zu Grunde liegende X;ML-Datei:
XML-Code:
<Catalog role="STANDARD">
  <businessSupplier companyName="DATAWERK" id="1">
    <businessRelationships>
      <businessRelationship id="1">

      </businessRelationship>
    </businessRelationships>
  </businessSupplier>
  <Categories>
    <Category name="Testkategorie 1" id="1">
      <items>
        <item name="Testitem 1.a" id="1" orderable="true">
          <shortdescription>Kurzbeschreibung des Produktes</shortdescription>
          <longdescription>Ausführliche, lange Beschreibung des Produktes</longdescription>
          <itemIdentifier>C3-45-87</itemIdentifier>
        </item>
        <item name="Testitem 1.b" id="2" orderable="true">
        </item>
        <item name="Testitem 1.c" id="3" orderable="true">
        </item>
        <item name="Testitem 1.d" id="4" orderable="true">
        </item>
      </items>
    </Category>
Das ist nur ein Auschnitt.
Jetzt wähle ich mittels XPath den Knoten items aus und iteriere durch die unterknoten:
Code:
nodeIter = nav.Select("/Catalog/Categories/Category[@name=\"" + name + "\"]/items/*");          
            while (nodeIter.MoveNext())
            {
                ProductItem prodItem = new ProductItem();
                prodItem.Name = nodeIter.Current.GetAttribute("name", "");
                nodeProduct = nav.Select("/Catalog/Categories/Category[@name=\"" + name + "\"]/items/item/shortdescription");
                nodeProduct.MoveNext();
                prodItem.ShortDescription = nodeIter.Current.Value;
                nodeProduct = nav.Select("/Catalog/Categories/Category[@name=\"" + name + "\"]/items/item/longdescription");
                nodeProduct.MoveNext();
                prodItem.LongDescription = nodeIter.Current.Value;
                nodeProduct = nav.Select("/Catalog/Categories/Category[@name=\"" + name + "\"]/items/item/itemIdentifier");
                nodeProduct.MoveNext();
                prodItem.ItemIdentifier = nodeIter.Current.Value;
                productList.Add(prodItem);              
            }
Innerhalb der Schleife versuche ich auf die Unterelemente eines Items zu zugreifen. Komischerweise steht nach dem Aufruf von
Code:
prodItem.ShortDescription = nodeIter.Current.Value;
auch schon alle anderen Elemente in der Eigenschaft. Er hat sie also irgendwie alle auf einmal ausgelesen. das ist natürlich nicht brauchbar. Was muss ich anders machen, damit ich gezielt auf die Elemente zugreifen kann?

Khabarakh 9. Mai 2008 12:59

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Das innere Select weiß ja nichts vom äußeren, genauer gesagt von dessen "Current"-Knoten. Also bekommst du mit "/item" im zweiten XPath wieder alle item-Elemente. Die zwei Abfragen müssen aufeinander aufbauen:
Code:
while (nodeIter.MoveNext()) {
   var nodeProduct = nodeIter.Current.Select("shortdescription");
   nodeProduct.MoveNext();
   Console.WriteLine(nodeProduct.Current.Value);
   [...]
PS: XPath ist quasi schon wieder out, lang lebe XLinq :mrgreen: . Aber da .Net 3.5 für euch wahrscheinlich ein no-go sein dürfte, dürfte XPath wirklich noch das Erträglichste sein.

Luckie 9. Mai 2008 13:24

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Auch wenn ich so mache, wie du habe ich wieder alle Werte:
Code:
nav = docNav.CreateNavigator();
            nodeIter = nav.Select("/Catalog/Categories/Category[@name=\"" + name + "\"]/items/*");          
            while (nodeIter.MoveNext())
            {
                ProductItem prodItem = new ProductItem();
                prodItem.Name = nodeIter.Current.GetAttribute("name", "");
                nodeProduct = nodeIter.Current.Select("shortdescription");
                nodeProduct.MoveNext();
                prodItem.ShortDescription = nodeIter.Current.Value;              
                productList.Add(prodItem);              
            }

Khabarakh 9. Mai 2008 15:10

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Die Eigenschaft nicht mit "nodeIter" sondern mit "productNode" befüllen ;) . Hatte es in meinem Code (WriteLine) korrigiert, aber vergessen, nochmal explizit darauf hinzuweisen.

Luckie 9. Mai 2008 19:59

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Jetzt sehe ich es auch. :wall:

Sascha L 9. Mai 2008 21:07

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Was für eine "Komponente" verwendest du?

Doch nicht XMLDocument, XMLNode, etc. oder?

Geht mich ja nichts an, aber "MoveNext" ist absolut unschön und fehlerträchtig. Überhaupt das durchgehen durch den XML-Baum.

Ich würde komplett auf XPATH setzen.

Mit SelectSingleNode oder SelectNodes das entsprechende auswählen, mit einer foreach-Schleife oder je nach Bedarf switch-case die einzelnen Elemente abfragen (Node.Name) und dann die Werte auslesen.

Luckie 9. Mai 2008 21:11

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Zitat:

Zitat von Sascha L
Was für eine "Komponente" verwendest du?

Keine. Ist ein .NE Projekt und ich benutze die enthaltene XML-Assemby.


Geht mich ja nichts an, aber "MoveNext" ist absolut unschön und fehlerträchtig. Überhaupt das durchgehen durch den XML-Baum.

Zitat:

Ich würde komplett auf XPATH setzen.
Das tue ich. Ist alles XPath.

Sascha L 9. Mai 2008 22:06

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Ja dass du mit .NET und C# arbeitest, ist mir klar.

Aber da selektiert man mit XPATH die Nodes oder den Node, die/den man haben will und arbeitet sich dann mit foreach durch die Nodelist ;-)


Folgender Code ist absolut sicher, egal wie die XML-Datei aussieht:

Code:
string name = "Testkategorie 1";
            XmlDocument XMLDoc = new XmlDocument();
            XMLDoc.LoadXml(textBox1.Text);
            List<ProductItem> prodList = new List<ProductItem>();
            XmlNodeList NodeList = XMLDoc.SelectNodes("//Catalog/Categories/Category[@name=\"" + name + "\"]/items/item");
            foreach (XmlNode ItemNode in NodeList)
            {
                ProductItem prodItem = new ProductItem();
                XmlNode NodeAttr = ItemNode.Attributes.GetNamedItem("name");
                if (NodeAttr != null)
                {
                    prodItem.Name = NodeAttr.Value;
                }
                foreach (XmlNode ItemChildNode in ItemNode.ChildNodes)
                {
                    switch (ItemChildNode.Name)
                    {
                        case "shortdescription": prodItem.ShortDescription = ItemChildNode.InnerText; break;
                        case "longdescription": prodItem.LongDescription = ItemChildNode.InnerText; break;
                        case "itemIdentifier": prodItem.ItemIdentifier = ItemChildNode.InnerText; break;
                    }
                }
                prodList.Add(prodItem);
               
            }
da ich den Code selbst getestst habe, sind im Code einige Dinge, die ich dafür benötigt habe (textbox1, usw.)

Luckie 9. Mai 2008 22:18

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Werde ich mir am Dienstag an der Arbeit mal etwas genauer angucken. Danke für den Hinweis.

Sascha L 9. Mai 2008 22:20

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Zitat:

Zitat von Luckie
Werde ich mir am Dienstag an der Arbeit mal etwas genauer angucken. Danke für den Hinweis.

Kein Problem ;-)

Am Anfang ist es normal beim Umstieg von Delphi nach C#, dass man die alten Gewohnheiten weiterpflegt. Wenn man aber erstmal das Konzept von C# verstanden hat, wird man nie wieder Delphi benutzen wollen ;-)

Luckie 9. Mai 2008 22:30

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Zitat:

Zitat von Sascha L
Am Anfang ist es normal beim Umstieg von Delphi nach C#, dass man die alten Gewohnheiten weiterpflegt. Wenn man aber erstmal das Konzept von C# verstanden hat, wird man nie wieder Delphi benutzen wollen ;-)

Das fürchte ich auch. Mit dem VS läuft das alles so schön geschmeidig von der Hand, macht richtig Spass. Delphi kommt einem da fast etwas unbeholfen und schwerfällig vor. :?

Khabarakh 9. Mai 2008 23:48

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Sascha, ich kann deine Argumentation leider nicht ganz nachvollziehen ;) . Erst einmal ist XPathDocument genau für solche Zwecke gedacht, bei denen man kein fettes DOM benötigt, sondern nur einen kleinen schnellen read-only Parser.
Zitat:

Zitat von Sascha L
Geht mich ja nichts an, aber "MoveNext" ist absolut unschön und fehlerträchtig. Überhaupt das durchgehen durch den XML-Baum.

Wer sagt, foreach funktioniere nur mit XmlDocument ;) ? Hier eine etwas schönere Variante meines Codes:
Code:
      static void XPathDoc()
      {
         var productList = new List<ProductItem>();

         var nav = new XPathDocument(new StringReader(xml)).CreateNavigator();
         foreach (XPathNavigator itemNav in nav.Select("/Catalog/Categories/Category[@name=\"" + name + "\"]/items/*")) {
            ProductItem prodItem = new ProductItem {
               Name = itemNav.GetAttribute("name", ""),
               ShortDescription = itemNav.SelectSingleNode("shortdescription").Value,
               LongDescription = itemNav.SelectSingleNode("longdescription").Value,
               ItemIdentifier = itemNav.SelectSingleNode("itemIdentifier").Value,
            };
            productList.Add(prodItem);
         }
      }
Den Code könnte man natürlich auch auf das Element-fehlt-Handling von dir umstellen, aber das muss Luckie wissen.
Und ich sehe auch nicht, dass du im Gegensatz zu uns etwas anderes machst als "das durchgehen durch den XML-Baum", oder wo du "komplett auf XPATH" setzt - genaugenommen sinds bei uns doch sogar 3 XPaths mehr :mrgreen: .
Zitat:

Zitat von Sascha L
Aber da selektiert man mit XPATH die Nodes oder den Node, [...]

Wie ich schon schrob, man benutzt heute eher XLinq - wenns einem gefällt :stupid: .
Code:
      static void LinqToXML()
      {
         var cat = XElement.Parse(xml).Element("Categories").Elements("Category").Single(node => node.Attribute("name").Value == name);

         var productList =
         (from item in cat.Element("items").Elements()
          select new ProductItem {
             Name = item.Attribute("name").Value,
             ShortDescription = item.Element("shortdescription").Value,
             LongDescription = item.Element("longdescription").Value,
             ItemIdentifier = item.Element("itemIdentifier").Value
          }).ToList();
      }
Manchen dürfte die Verbosity nicht gefallen, aber man kann immer noch entgegnen "hmmm, und wo genau in deinem Code ist nun ein Schutz gegen XPath-Injection?" :mrgreen: . Und in Sachen Performance ist nicht alles von 3.0+ miserabel...
Code:
   1. LinqToXML                        132 µs
2. XmlDoc                           186 µs
3. XPathDoc                         245 µs
Getestet mit 20 Produkten. Bei wenigern wird XPathDocument zweiter, mein Eingangssatz über dessen Performance dürfte damit trotzdem widerlegt sein :stupid: .

Um die Verwirrung zu komplettieren, sollte man noch Linq To XSD im Auge behalten. 6 Wege zum gleichen Ziel, _das_ ist .Net!

Sascha L 10. Mai 2008 08:06

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Ja, der erste Code von dir ist nun besser. Natürlich fehlt noch - wie du selbst sagtest - die Überprüfung, ob die Nodes und Attribute überhaupt existieren.

Aber der Urpsprungscode mit MoveNext() war nicht so gut, da man ja nie weiß wo ma da landet. Alleine schon wegen des "*" am Ende der XPath-Abfrage würde man theoreitsch alle möglichen Child-Nodes durchgehen, obwohl man ja nur "item" in "items" haben will.

Auch wenn ich die XML-Datei selbst erstelle, würde ich das ganze immer allgemein programmieren. Da ist man dann flexibler, wenn man später doch noch mal das ein oder andere an der XML-Datei ändert - was meines erachtens zu 99% der Fall sein wird :D


Zitat:

Zitat von Luckie
Das fürchte ich auch. Mit dem VS läuft das alles so schön geschmeidig von der Hand, macht richtig Spass. Delphi kommt einem da fast etwas unbeholfen und schwerfällig vor. :?

Wieso "fürchtest" du das auch? Delphi ist nun mal leider ausgestorben, auch wenn es viele nicht wahrhaben wollen. Delphi war wirklich ne tolle Sprache. Ich habe damit fast 10 Jahre programmiert, aber mittlerweile ist sie einfach zu alt. Delphi ist nicht mehr leistungsstark, bietet keine modernen Features und das Angebot an Komponenten von Drittherstellern kann man an einer Hand abzählen. Des Weiteren weiß jeder, wieder schlecht die IDE ist.

In meinem Bekanntenkreis habe ich bisher noch jeden Delphi-User dazu bringen können, dauerhaft zu C# zu wechseln und bei uns in der Firma haben wir diesen Schritt nun auch vollzogen.

Bereits in 5min wird einem klar, dass C# und VS besser ist:
- Visual Studio ist SOFORT gestartet. Man brauch nicht mehr wie beim Developer Studio erst noch ne Tasse Kaffee trinken
- Die Auswahl an Controls von Fremdanbietern ist überwältigend
- Wenn ich überlege, wie oft ich umständlich Klassen in Delphi anlegen musste und total viel Code schreiben musste, nur um eine Liste von einem eigenen Typ zu haben! In .NET nimmt man einfach List<TYP> und gut ist. Das ist eines der Killerkriterien!
- foreach ist super
- die ganzen Konvertierungen á la ".ToString()
- settings.settings! NIE wieder Registry, Ini und man muss sich nie wieder Gedanken machen wie man bestimmte Objekte speichern und laden kann
- Man hat viel leicht Zugriff auf bestimmte APIs, Funktionen des Betriebsystems, etc.
- im MSDN befindet sich zu allem auch ein C#-Beispiel (Wie oft musste man mühsehlig den Code nach Delphi übersetzen?!)
- Datenbankanbindung, Lynq, usw
- .NET ist immer auf dem neuesten Stand: z.B. Vista-Unterstützung (Glass-Effekte, etc.)
- Ich programmiere EIN Programm, welches ohne weiteres auf dem PC und auf Windows Mobile (CompactFramework) läuft!!!

Da wir grad in der Firma ein Programm in .NET komplett neu machen, können wir sehr schön den Unterschied sehen: Der C#-Code ist nur ein Bruchteil so lang wie jener von Delphi damals.

Luckie 13. Mai 2008 10:36

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Zitat:

Zitat von Khabarakh
Wer sagt, foreach funktioniere nur mit XmlDocument ;) ? Hier eine etwas schönere Variante meines Codes:
Code:
      static void XPathDoc()
      {
         var productList = new List<ProductItem>();

         var nav = new XPathDocument(new StringReader(xml)).CreateNavigator();
         foreach (XPathNavigator itemNav in nav.Select("/Catalog/Categories/Category[@name=\"" + name + "\"]/items/*")) {
            ProductItem prodItem = new ProductItem {
               Name = itemNav.GetAttribute("name", ""),
               ShortDescription = itemNav.SelectSingleNode("shortdescription").Value,
               LongDescription = itemNav.SelectSingleNode("longdescription").Value,
               ItemIdentifier = itemNav.SelectSingleNode("itemIdentifier").Value,
            };
            productList.Add(prodItem);
         }
      }
Den Code könnte man natürlich auch auf das Element-fehlt-Handling von dir umstellen, aber das muss Luckie wissen.

Jupp, die wird benötigt. Ich habe das jetzt mal auf deinen obigen Code umgestellt. Wie kann ich da reagieren, wenn ein Element fehlt?

Sascha L 13. Mai 2008 14:33

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Genau wie bei meiner Variante:

Code:
XMLNode Node = itemNav.SelectSingeNode("...");
if (Node != null)

Luckie 13. Mai 2008 14:58

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
OK, dann habe ich es ja richtig gemacht.

Luckie 29. Mai 2008 12:44

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Jetzt habe ich schon wieder das Problem. :wall:

XML-Datei:
XML-Code:
<businessRelationship id="1">
    <businessSupplier id="1">
        <company>
            <companyName>DATAWERK</companyName>
            <websiteList>
                <website primary="true">www.datawerk.de</website>
                <website>www.datawerk.com</website>
            </websiteList>
            <addressList>
                <address primary="true" id="1">
                    <street>Friedrich-Ebert-Straße</street>
Und ich will jetzt das Element [i]companyName" auslesen:
Code:
        public void GetCompanyDetails()
        {
            try
            {
                XPathNavigator nav = pathDoc.CreateNavigator();
                XPathNodeIterator nodeIter = nav.Select("company");
                nodeIter.MoveNext();
                XPathNodeIterator companyItem = nodeIter.Current.Select("companyName");
                companyItem.MoveNext();
                if (companyItem != null)
                {                   
                    companyName = companyItem.Current.Value;                  
                }
            }
            catch(Exception e)
            {
                throw(e);
            }
        }
Aber ich bekomme wieder die Werte aller Unterelemente in einem String. :evil:

Khabarakh 29. Mai 2008 13:38

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Der erste XPath kann so nicht funktionieren: Ohne angegebene Achse sucht er in den Kindern des derzeitigen Knotens, der Wurzel. Und dort gibt es kein "company". Was ich allerdings noch nicht verstanden habe: Anstatt eine Exception zu werfen oder ähnliches, gibt er einfach die Wurzel zurück :shock: ? Damit zeigt Current im inneren Block immer noch auf die Wurzel und du bekommst den Text des gesamten Dokuments zurück.
Als ersten Ausdruck könntest du stattdessen "//company" benutzen. Ich würde aber zur Sicherheit den vollen Pfad von der Wurzel aus angegben, also "/A/B/...". Wie du siehst, bin ich aber auch nicht ganz per Du mit XPath, und bei solchen Schauermärchen habe ich es in nächster Zeit auch nicht vor :duck: .

Luckie 29. Mai 2008 13:44

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Oh Gott, es funktioniert mit "//". Was hat den das für eine Bedeutung?

marabu 29. Mai 2008 16:58

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Hallo Michael,

der Double-Slash erweitert den Suchraum auf einen SubTree. "/root/test" findet alle Knoten "test" auf level 2, "//test" findet alle Knoten "test", egal auf welchem Level.

Freundliche Grüße

Sascha L 29. Mai 2008 22:13

Re: Mit XPath Knoten auswählen liefert alle Knotenwerte
 
Hi Luckie,

wie ich auch schon in einem anderen Thread gepostet habe:

Hier findest du sehr leicht zu verstehende Beispiele: http://www.zvon.org/xxl/XPathTutoria.../example1.html Wenn man sich die Beispiele einmal angeschaut hat, wird man schnell merken, dass XPath eigentlich gar nicht so kompliziert ist.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:17 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz