Xml-Tutorial

    • Allgemein

    Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

      Xml-Tutorial

      Vor längerer Zeit habichmich mal mit Xml beschäftigt, und sogar ein Tut dazu verzapft, wassich euch hiermit antue. Inzwischen bin ich wieder sehr von Xml abgekommen, nämlich seit ich das typisierte Dataset näher kennengelernt habe.
      Dieses speichert sich nämlich auch als Xml ab, hat aber erhebliche Vorteile gegenüber Xml (und keinen Nachteil):
      Xml ist ein reines Text-Format. Alle Daten ausser String müssen also in String übersetzt und rück-übersetzt werden - eine mühsame und fehlerträchtige Fummel-Arbeit, die einem vom Dataset komplett abgenommen wird.
      Xml kann genuin "nur" Baumstrukturen abbilden - das Dataset hingegen ist ein voll entwickeltes relationales Datenmodell.
      Egal - hier also, wassich damals über Xml in Erfahrung gebracht habe:

      Zum Abschnitt "XML parsen": wesentliche Verbesserungen ab VS2010'
      Hier der Link zu einem "moderneren Tut", mit Video sogar:
      Xml verarbeiten mit Intellisense (Schema und XDocument)
      (Dieses tolle Feature, dass Intellisense beim schreiben der Abfragen hilft, ist leider in c# nicht verfügbar.)


      XML – was ist das?
      XML (Extendet Markup Language) ist eine sog. "Auszeichnungssprache", mit absolut minimaler Syntax:
      Inhalte werden in sie bezeichnende Start- und End-Tags eingeschlossen ("ausgezeichnet"). Den kompletten Tag (Start-Tag, Inhalt, End-Tag) kann man als Knoten bzw. Element bezeichnen. Knoten können ineinandergeschachtelt werden, d.h. ein Knoten kann mehrere untergeordnete Knoten enthalten.
      Ein Xml-Dokument enthält im Grunde nur einen einzigen Knoten (den Root-Knoten, auch genannt "DocumentElement"), in den die anderen hineingeschachtelt sind. Mit dieser hierarchischen Datenstruktur kann man recht komplexe Sachverhalte eindeutig abbilden:
      Beispiel

      XML-Quellcode

      1. <XmlSettings LastClosingTime="01:16:16">
      2. <MDIParent Left="22" Top="22" Width="648" Height="400">
      3. <MDIChildren>
      4. <MDIChild Left="0" Top="0" Width="112" Height="100">
      5. <TextboxText>Dieses ist der eine Text.</TextboxText>
      6. </MDIChild>
      7. <MDIChild Left="221" Top="77" Width="184" Height="88">
      8. <TextboxText>Dieses ist der andere Text.</TextboxText>
      9. </MDIChild>
      10. </MDIChildren>
      11. </MDIParent>
      12. </XmlSettings>

      Erläuterung:
      Der Root-Knoten "XmlSettings" hat einen Knoten "MDIParent" zum Inhalt, welcher einen Knoten "MDIChildren" enthält, welcher wiederum zwei Knoten namens "MDIChild" enthält, von denen jeder einen Knoten "TextboxText" beinhaltet.
      ("TextboxText" beinhaltet Text, und das ist streng genommen auch ein Knoten, aber einer, in den man nichts mehr hineinschachteln kann)
      Das Beispielprojekt generiert und positioniert anhand dieser Informationen ein MDIParent-Form mit zwei MDIChild-Forms (untergeordnete Fenster innerhalb des Haupt-Fensters der Anwendung). Position und (in einer Textbox) anzuzeigender Text der Children ist dank der Verschachtelung eindeutig zugeordnet.
      Wie zu sehen, kann der Start-Tag eines Knotens noch zusätzliche Informationen enthalten, die Attribute nämlich.
      Ja, damit ist auch schon das wesentlichste vom Xml-Format erzählt.
      Hier eine Abbildung des Beispiels in Struktur-Ansicht:


      Sonder-Tags
      Darüberhinaus gibt es noch eine Reihe von Sonder-Tags, die nicht aus Start, Inhalt, End-Tag bestehen, sondern mit nur einer <> ihre Daseinsberechtigung haben, beispielsweise der
      <!--Kommentar-Tag-->
      oder die Xml-Deklaration:
      <?xml version="1.0" encoding="utf-8"?>.
      Einen Tag ohne hineingeschachtelten Inhalt schreibt man verkürzt
      <LeererTag />,
      und der braucht ebenfalls keinen End-Tag (kann aber sehr wohl Attribute enthalten).
      Achtung: Diese Liste ist nicht vollständig!
      Xml parsen
      Wie analysiere ich nun so eine Xml-Datei, sodaß ich die niedergelegten Daten im Programmcode verfügbar bekomme (kurz gesprochen: "wie parse ich")?
      Zum Beispiel (VB.Net) so:

      VB.NET-Quellcode

      1. Private Sub LoadAndProcessSettings()
      2. If Not File.Exists(XIniFullname) Then Return
      3. Dim XDoc As New System.Xml.XmlDocument
      4. XDoc.Load(XIniFullname)
      5. lbLastUsed.Text = XDoc.DocumentElement.Attributes("LastClosingTime").Value
      6. Dim ndMDIParent As XmlElement = _
      7. DirectCast(XDoc.SelectSingleNode("XmlSettings/MDIParent"), XmlElement)
      8. With Me 'MDIParent positionieren
      9. .Left = Integer.Parse(ndMDIParent.GetAttribute("Left"))
      10. .Top = Integer.Parse(ndMDIParent.GetAttribute("Top"))
      11. .Width = Integer.Parse(ndMDIParent.GetAttribute("Width"))
      12. .Height = Integer.Parse(ndMDIParent.GetAttribute("Height"))
      13. End With
      14. For Each xelMDIChild As XmlElement In ndMDIParent.SelectNodes(xpath:="MDIChildren/MDIChild")
      15. With New frmToAdd 'MDICHild positionieren, weitere Eigenschaften festlegen
      16. .MdiParent = Me
      17. .Left = Integer.Parse(xelMDIChild.GetAttribute("Left"))
      18. .Top = Integer.Parse(xelMDIChild.GetAttribute("Top"))
      19. .Width = Integer.Parse(xelMDIChild.GetAttribute("Width"))
      20. .Height = Integer.Parse(xelMDIChild.GetAttribute("Height"))
      21. Dim ndText As XmlNode = xelMDIChild.SelectSingleNode("TextboxText")
      22. .TextBox1.Text = ndText.InnerText
      23. .Show()
      24. End With
      25. Next
      26. End Sub

      Erläuterung
      Wie zu sehen, die eigentliche Parserei (das wären String-Operationen ohne Ende!) erledigt das XmlDocument-Objekt für mich. Ich brauche nur zu laden, und kann dann die Knoten abrufen; kann deren Attribut-Werte, deren InnerText direkt im Code einsetzen.
      Im Grunde brauche ich kaum noch was zu erläutern, steht ja alles da.
      .SelectNodes()
      Auf .SelectNodes(XPath as String) will ich noch eingehen, weil das ist schon fast ein kleiner Datenbank-Schlingel:
      Beispielsweise der XPath "XmlSettings/MDIParent/MDIChildren/MDIChild/TextboxText" bezeichnet alle im Knoten "XmlSettings"enthaltenen Knoten "MDIParent" (das ist nur einer), darin alle Knoten "MDIChildren" (auch nur einer), darin alle "MDIChild" (das sind zwei!), darin alle "TextboxText"-Knoten (je einer).
      XDoc.SelectNodes(XPath) gibt mir also 2 "TextboxText"-Knoten zurück.

      Ich könnte mir auch eine XmlNodelist nur von Attribut-Knoten ausgeben lassen:

      VB.NET-Quellcode

      1. Dim XAttributes As XmlNodeList = _
      2. XDoc.SelectNodes("XmlSettings/MDIParent/MDIChildren/MDIChild/@Width")

      ...Und dann nach einem MDIChild bestimmter Breite suchen:

      VB.NET-Quellcode

      1. Dim ndMDIChild As XmlNode
      2. For Each XAttr As XmlAttribute In XAttributes
      3. If XAttr.Value = "112" Then
      4. ndMDIChild = XAttr.OwnerElement
      5. Exit For
      6. End If
      7. Next

      Das geht mit XPath übrigens noch einfacher:

      VB.NET-Quellcode

      1. Dim ndMDIChild As XmlNode = XDoc.SelectNodes( _
      2. "XmlSettings/MDIParent/MDIChildren/MDIChild[@Width=112]")

      Sowohl das XmlDocument-Objekt, als auch jeder untergeordnete Knoten kann aus seinen untergeordneten Knoten selektieren (wobei .SelectSingleNode() keine XmlNodeList zurückgibt, sondern den 1. gefundenen Knoten).
      Achtung: .SelectNodes() ist case-sensitiv, also Groß / Kleinschreibung beachten!

      Settings abspeichern
      So, hamwa also schön geparst, jetzt wollnwa bei Programmende die neuen Settings aber auch wieder abspeichern.
      Hier macht sich das XmlDocument-Objekt ebenso nützlich, es erzeugt die erforderlichen Knoten und Attribute, und ich kann deren Eigenschaften festlegen, und sie dann an der richtigen Stelle einhängen.
      Fürs Erzeugen und Einhängen von XmlElementen habe ich ein Helferlein geschrieben:

      VB.NET-Quellcode

      1. Private Function AppendElement(ByVal ndParent As XmlNode, ByVal ElName As String) As XmlElement
      2. 'XmlElement erzeugen und in ndParent einhängen, das neue XmlElement zurückgeben
      3. Dim XDoc As XmlDocument
      4. If ndParent.NodeType = XmlNodeType.Document Then 'ndParent ist das OwnerDocument selbst
      5. XDoc = DirectCast(ndParent, XmlDocument)
      6. Else
      7. XDoc = ndParent.OwnerDocument
      8. End If
      9. AppendElement = XDoc.CreateElement(ElName)
      10. ndParent.AppendChild(AppendElement)
      11. End Function

      SaveSettings() macht dann 3 mal Gebrauch von diesem Helferlein:

      VB.NET-Quellcode

      1. Private Sub SaveSettings()
      2. Dim XDoc As New XmlDocument
      3. Dim xelRoot, xelMDIParent, xelMDIChildren, xelMDIChild, xelTextboxText As XmlElement
      4. Dim XDecl As XmlDeclaration = XDoc.CreateXmlDeclaration("1.0", "utf-8", "")
      5. XDoc.AppendChild(XDecl)
      6. Dim XComment As XmlComment = XDoc.CreateComment("Programmsettings im Xml-Format")
      7. XDoc.AppendChild(XComment)
      8. xelRoot = AppendElement(XDoc, "XmlSettings")
      9. xelRoot.SetAttribute("LastClosingTime", Format(Now, "Long Time"))
      10. xelMDIParent = AppendElement(xelRoot, "MDIParent")
      11. With xelMDIParent
      12. .SetAttribute("Left", Me.Left.ToString)
      13. .SetAttribute("Top", Me.Top.ToString)
      14. .SetAttribute("Width", Me.Width.ToString)
      15. .SetAttribute("Height", Me.Height.ToString)
      16. End With
      17. xelMDIChildren = AppendElement(xelMDIParent, "MDIChildren")
      18. For Each Frm As frmToAdd In Me.MdiChildren
      19. xelMDIChild = AppendElement(xelMDIChildren, "MDIChild")
      20. With xelMDIChild
      21. .SetAttribute("Left", Frm.Left.ToString)
      22. .SetAttribute("Top", Frm.Top.ToString)
      23. .SetAttribute("Width", Frm.Width.ToString)
      24. .SetAttribute("Height", Frm.Height.ToString)
      25. End With
      26. xelTextboxText = AppendElement(xelMDIChild, "TextboxText")
      27. xelTextboxText.InnerText = Frm.TextBox1.Text
      28. Next
      29. XDoc.Save(XIniFullname)
      30. End Sub


      Wie zu sehen: Am Schluß wird XDoc wieder ordentlich an den Ort gesaved, von dem's auch geladen wurde.

      Zusammenfassung Beispielprojekt
      Das Beispiel setzt Xml als Ersatz (m.E. besser: "Nachfolger") der klassischen Windows - Ini-Datei ein, und es zeigt sich, daß Xml komfortabler in der Anwendung ist (keine API-Deklarationen), v.a. aber auch mächtiger, denn mehrere MDIChildren mit je unterschiedlichen Werten zu initialisieren, damit ist die Syntax der Ini-Dateien überfordert, da sie keine Verschachtelung kennt.
      (Begriffs-)Verwirrung - Knoten ist nicht gleich Knoten!
      Der XmlDocument-Parser zerstückelt den Xml-Code nicht einfach in Knoten, sondern in Knoten verschiedenen Typs.
      Ein Xml-Attribut erscheint geparst als Knoten vom Typ "XmlNodeType.Attribute", welchselber eingehängt ist in einen (Parent-)Knoten vom Typ "XmlNodeType.Element".
      Allerdings wird das XmlAttribute nicht in die .ChildNodes – Eigenschaft des Parent-Knotens eingehängt, sondern in dessen .Attributes – Eigenschaft.
      Text dagegen erscheint als Knoten vom Typ "XmlNodeType.Text", und wird zusammen mit anderen untergeordneten Knoten in die .ChildNodes – Eigenschaft des Parent-Knotens eingehängt (Das kann man auch in obiger Struktur-Ansicht sehen).
      Der Tag <TextboxText ForeColor="Red">Innerer Text</TextboxText>
      wird also zu 3 Knoten geparst, ein XmlElement ein XmlAttribute und ein XmlText, wobei die letzteren beiden in verschiedenen (Auflistungs-)Eigenschaften des ersteren herumhängen.
      Alle diese Knoten sind spezielle Ausformungen der Grundform XmlNode, "Erben", um mit OOP zu sprechen.
      Mit welchem Typ Knoten man im Code zu tun hat kann man an dessen .NodeTyp – Eigenschaft ablesen (so geschehen in der Function AppendElement()).
      Wichtige NodeTypes:

      Quellcode

      1. VB-Objekt Eigenschaft Xml-Beispiel
      2. XmlNode ein Knoten im allgemeinen -
      3. XmlComment Kommentar <!--Kommentar-->
      4. XmlElement verschachtelungsfähig, hieraus <Elementname>...</Elementname>
      5. wird die hierarchische Struktur bzw.
      6. aufgebaut <Elementname /> (wenn leer)
      7. XmlAttribute einem XmlElement hinzugefügte <Elementname AttrName="AttrValue" />
      8. Information, wird in dessen (leeres XmlElement mit einem Attribut)
      9. .Attributes-Eigenschaft
      10. eingehängt
      11. XmlDocument Stamm-Objekt (= Root) wird -
      12. selbst auch als Knoten aufgefasst
      13. XmlDocumentType ein Verweis auf eine DTD-Datei, <!DOCTYPE XmlIni SYSTEM "XmlIni.dtd">
      14. anhand der die Gültigkeit der in
      15. diesem Xml-Document abgelegten
      16. Daten geprüft wird
      17. XmlText in ein Element geschachtelter Text <Elementname>Text</Elementname>
      18. XmlWhiteSpace Space, Linefeed etc., zur <Elementname> </Elementname>
      19. leserlichen Gestaltung
      20. XmlDeclaration Version- und Codierungs- <?xml version="1.0" encoding="utf-8"?>
      21. Information für den Xml-Parser

      Diese Tabelle ist nicht vollständig, und die Erläuterungen sind nur bedingt korrekt. Im ObjectBrowser der .Net-IDE findet sich die korrekte Auflistung incl. Erläuterungen unter "System.Xml.XmlNodeType"
      Dateien

      Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „ErfinderDesRades“ ()

      Weitere Möglichkeiten
      Ein XmlDocument kann sich per .LoadXml(sXml as String) auch mit einem String füttern lassen. So kann man etwa sehr komplexe Anweisungen einfach per Komandozeile an ein Hilfsprogramm übergeben (vielleicht eine Liste mit Ordnern, Dateinamen, Extensions etc. an ein Zipp-Programm geben).
      Beispiele für Xml
      Der Windows MediaPlayer speichert seine Wiedergabelisten in Xml ab, die VB.Net IDE hält ihre Projektkonfigurationen (.vbproj-Dateien, sehr interessant!) in Xml.

      Validierung
      Man kann natürlich mit Leichtigkeit kleine und große Fehler in so eine Xml-Datei einbauen, vielleicht ein Attribut vergessen, auf das das verarbeitende Programm versucht zuzugreifen, oder falsch schreiben, etwas in der Art.
      DTDs
      Dafür gibt's die DTD-Files die "Document Type Definitions". Ein sauber geproggtes Xml-File enthält einen <!DocType ...> - Tag, der auf ein DTD-File verweist, anhand dessen automatisch die Gültigkeit dieses Xml-Files geprüft wird (ob die richtigen Knoten in erlaubter Anzahl in die entsprechenden Ober-Knoten hineingeschachtelt sind, ob die richtigen Attribute vorhanden und ob die Werte vom richtigen DatenTyp sind)
      Die DTD-Syntax ist schon wesentlich komplizierter als Xml, und anstatt sie hier zu erläutern verweise ich auf SelfHTML, das enthält Referenz-mäßige Kapitel über Xml und DTD, sowie auf den AVB-Tipp 0597, wo man eine DTD im Einsatz sehen kann.
      Schemas
      Nachfolger der DTDs will wohl die Schema-Technologie werden, deren Syntax wesentlich genauere Definition der erwarteten Xml-Entitäten bereitstellt. Leider ist sie auch entsprechend umständlicher; bei einer DTD kann man die Wirkungsweise noch intuitiv einigermaßer erfassen (und vielleicht auch mal ein Definitiönchen anfügen), bei den Schemas scheint man mir ohne Tool entweder aufgeschmissen oder OberCrack zu sein.
      Zum (.Net-Progger-) Glück hat die .Net-IDE ein solches Tool integriert, und stellt beim Öffnen eines Xml-Documentes den Menü-Item "XML" bereit. Hier kann man für ein Xml-File sich ein passendes Schema generieren lassen (den Verweis-Tag darauf fügt die IDE auch gleich in den Xml-Code ein), und selbiges in der Schema-Designer-Ansicht auch komfortabel weiter anpassen.
      Weiterführendes über die Schema-Syntax fand ich auf folgender Site:
      Aufbau und Bedeutung der XML Schema Sprache und Vergleich mit der DTD-Sprache
      (Dieser Link führt zum eigentlichen Anfang der Site, aber mein Word zeigt den nicht an - Na in HTML sollte's aber funzen)
      Es handelt sich um die Seminar-Ausarbeitung eines Uni-Seminars der FH Wedel (und da gibt's noch etliche weitere Ausarbeitungen, zu verwandten, und auch zu nicht verwandten Themen)
      Datenbank-Integration
      .Net hat Xml auch sonst vielfältig in die Datenbank-programmierung integriert, so kann das .Dataset-Objekt befüllt werden sowohl über eine Datenbank-Connection, als auch einfach über den Befehl Dataset.LoadXml(Filename) - und dann liegt der Xml-Inhalt auf einmal als Datenbank vor, mit fix und fertig verknüpften Tabellen, Schlüsselspalten, Constraints, das ganze Zeugs. Und das evtl. gegebene Schema ist auch geladen und verhindert Fehleingaben, etwa im DatagridView.
      Was noch
      Xml-Transformation
      Wieder bei SelfHTML ist erläutert, wie man mit einem Xml-Verweis auf ein sog. XSL-Sheet einen Browser anweisen kann, aus dem Xml-Code einen HTML-Code zu generieren und anzuzeigen. Die Xml-Datenbank kann also ihren eigenen Daten-Viewer mitliefern, und der sieht wirklich gut aus!

      Wo mans richtig lernen kann:
      Na, gewissermaßen beim Erfinder persönlich, d.i.: W3Schools