Xml minimal auslesen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    Xml minimal auslesen

    Hallo,

    ich habe hier eine Anwendung, die aus einem Xml-File eine Liste erzeugen soll. Die xml beinhaltet Produkte und einige Informationen zu diesen u.a. den Preis.
    Dabei sei das Level auf dem die Produkte und Preise auftauchen beliebig, bis auf dass das Level der Preise ein Subnode der Produkte sein muss.

    Die Anwendung definiert Klassen basierend auf dem Aufbau der Xml und deserialisiert die xml und ist damit nur soweit flexibel, wie alle Level ihre Hierarchie beibehalten.
    Zum Beispiel gibt es da mehrere Subnodes Produkt im Node Produkte. Der Node Produkte enthält sonst nichts, kann man bspw. solche Hüllnodes irgendwie automatisch überspringen?

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub pnDropXml_DragDrop(sender As Object, e As DragEventArgs) Handles pnDropXml.DragDrop
    3. Dim files = DirectCast(e.Data.GetData(DataFormats.FileDrop), String())
    4. If files(0).EndsWith(".xml") Then
    5. Using flStrm As New FileStream(files(0), FileMode.Open)
    6. ListPricesFrom(flStrm)
    7. End Using
    8. End If
    9. End Sub
    10. Private Sub pnDropXml_DragEnter(sender As Object, e As DragEventArgs) Handles pnDropXml.DragEnter
    11. If e.Data.GetDataPresent(DataFormats.FileDrop) Then
    12. e.Effect = DragDropEffects.Copy
    13. End If
    14. End Sub
    15. Private Sub ListPricesFrom(stream As Stream)
    16. rtbResult.ResetText()
    17. Dim serializer As New XmlSerializer(GetType(XMLBody))
    18. Dim X = DirectCast(serializer.Deserialize(stream), XMLBody)
    19. For Each prod In X.Angebot.Produkte
    20. rtbResult.AppendText($" {prod.Id}:{vbCrLf}{prod.Referenz.Preis}{vbCrLf}")
    21. Next
    22. End Sub
    23. End Class
    24. Public Class XMLBody
    25. Public Property Angebot As Angebot
    26. End Class
    27. Public Class Angebot
    28. Public Property Produkte As List(Of Produkt)
    29. End Class
    30. Public Class Produkt
    31. Public Property Id As String
    32. Public Property Referenz As Referenz
    33. End Class
    34. Public Class Referenz
    35. Public Property Preis As String
    36. End Class


    Viele Grüße

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Wenn die "Levels" wirklich unterschiedlich sind, wirst du vermutlich selbst parsen müssen. Ich würde eine rekursive Funktion bauen die ein XmlNode oder XmlElement als Argument annimmt, so dann komplett durch iterieren wie bei einer rekursiven Dateisuche.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    Du könntest sowas vielleicht machen. Der XPath-Ausdruck "//Produkt" selektiert alle Nodes mit dem Namen Produkt, egal wo sie im Dokument stehen. Ich bin mir aber nicht sicher bzgl. des Serializers und des XmlNodeReader. Brauchst du das wirklich so, dass du ein Objekt aus dem Xml erzeugst? Arbeite evtl. mit dem Dokument "manuell", gerade wenn du die Werte nur ausgeben möchtest.

    C#-Quellcode

    1. void Main()
    2. {
    3. const string xmlFilePath = @"Test.xml";
    4. XmlDocument xmlDocument = new();
    5. xmlDocument.Load(xmlFilePath);
    6. XmlNodeList productXmlNodeList = xmlDocument.SelectNodes("//Produkt");
    7. XmlSerializer serializer = new(typeof(Produkt));
    8. foreach (XmlNode productNode in productXmlNodeList)
    9. {
    10. XmlNodeReader xmlNodeReader = new(productNode);
    11. Produkt produkt = (Produkt)serializer.Deserialize(xmlNodeReader);
    12. // hier weitermachen
    13. }
    14. }
    15. // You can define other methods, fields, classes and namespaces here
    16. public class Produkt
    17. {
    18. public string Id {get;set;}
    19. public Referenz Referenz{get;set;}
    20. }
    21. public class Referenz
    22. {
    23. public string Preis {get;set;}
    24. }

    Haudruferzappeltnoch schrieb:

    aber was meinst du mit "manuell"

    Damit meine ich, dass du anstelle des Serializers mit dem XmlDocument/XmlNodeList weiter arbeitest. Z.B. so

    C#-Quellcode

    1. void Main()
    2. {
    3. const string xmlFilePath = @"Test.xml";
    4. XmlDocument xmlDocument = new();
    5. xmlDocument.Load(xmlFilePath);
    6. XmlNodeList productXmlNodeList = xmlDocument.SelectNodes("//Produkt");
    7. foreach (XmlNode productNode in productXmlNodeList)
    8. {
    9. XmlNode priceXmlNode = productNode.SelectSingleNode("Referenz/Preis");
    10. priceTextBox.Text = priceXmlNode.InnerText;
    11. }
    12. }

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ISliceUrPanties“ ()

    @ErfinderDesRades Ja das funktioniert, aber wenn ich Produkte entferne und Produkt nur noch in der Angebot Node stehen, schon nicht mehr. Halt unflexibel.

    @ISliceUrPanties Ich habe es so noch nicht hinbekommen, bin mit der Notation etwas unvertraut, er meldet mir Produkt wäre ein ungültiger Token bei .SelectNodes Der Unterschied von / und \ ^^
    Ja so geht es schon flexibler, und die Klassen muss ich auch nicht mehr erstellen:
    Muss ich das XmlDocument irgendwie aufräumen, es gibt kein Dispose?

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub pnDropXml_DragDrop(sender As Object, e As DragEventArgs) Handles pnDropXml.DragDrop
    3. Dim files = DirectCast(e.Data.GetData(DataFormats.FileDrop), String())
    4. If files(0).EndsWith(".xml") Then
    5. Using flStrm As New FileStream(files(0), FileMode.Open)
    6. ListCodesFrom2(flStrm)
    7. End Using
    8. End If
    9. End Sub
    10. Private Sub pnDropXml_DragEnter(sender As Object, e As DragEventArgs) Handles pnDropXml.DragEnter
    11. If e.Data.GetDataPresent(DataFormats.FileDrop) Then
    12. e.Effect = DragDropEffects.Copy
    13. End If
    14. End Sub
    15. Private Sub ListCodesFrom2(stream As Stream)
    16. rtbResult.ResetText()
    17. Dim xmlDoc As New XmlDocument
    18. xmlDoc.Load(stream)
    19. Dim Produkte = xmlDoc.SelectNodes("//Produkt")
    20. For Each prod As XmlNode In Produkte
    21. rtbResult.AppendText($" {prod.SelectSingleNode("Id").InnerText}:{vbCrLf}{prod.SelectSingleNode("Referenz/Preis").InnerText}{vbCrLf}")
    22. Next
    23. End Sub
    24. End Class

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    Halt unflexibel.

    Das ist aber auch gar nicht der Zweck von Xml. Es soll(te) einmalig ein standardisiertes Format definiert werden, mit dem gearbeitet werden kann/soll. Wenn es nur für dich ist, ist eine Änderung an der Struktur kein Problem. Aber im Normalfall bedeutet eine Änderung, dass eine neue Version definiert wird und es einen Upgrade-Pfad geben muss. Halt ein Änderungsprozess.

    Haudruferzappeltnoch schrieb:

    es gibt kein Dispose?

    Nein, es wird ja IDisposable nicht implementiert. Dein Stream wird doch später wieder geschlossen - um den Rest kümmert sich der GC.
    Komplexe Klaseninstanzen müssen nicht zwangsweise explizit entsorgt/disposed werden. Das ist nur nötig, wenn sie Objekte/Handles erschaffen und/oder Zugriff darauf nehmen, an die der GC nicht rankommt. Das ist bei nem XmlFile-Objekt nicht der Fall. Sehr schön! Eine diesbezüglich unkomplizierte Klasse weniger ;)
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.