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'
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
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:
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:
...Und dann nach einem MDIChild bestimmter Breite suchen:
Das geht mit XPath übrigens noch einfacher:
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:
SaveSettings() macht dann 3 mal Gebrauch von diesem Helferlein:
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:
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"
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:
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 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
- <XmlSettings LastClosingTime="01:16:16">
- <MDIParent Left="22" Top="22" Width="648" Height="400">
- <MDIChildren>
- <MDIChild Left="0" Top="0" Width="112" Height="100">
- <TextboxText>Dieses ist der eine Text.</TextboxText>
- </MDIChild>
- <MDIChild Left="221" Top="77" Width="184" Height="88">
- <TextboxText>Dieses ist der andere Text.</TextboxText>
- </MDIChild>
- </MDIChildren>
- </MDIParent>
- </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
- Private Sub LoadAndProcessSettings()
- If Not File.Exists(XIniFullname) Then Return
- Dim XDoc As New System.Xml.XmlDocument
- XDoc.Load(XIniFullname)
- lbLastUsed.Text = XDoc.DocumentElement.Attributes("LastClosingTime").Value
- Dim ndMDIParent As XmlElement = _
- DirectCast(XDoc.SelectSingleNode("XmlSettings/MDIParent"), XmlElement)
- With Me 'MDIParent positionieren
- .Left = Integer.Parse(ndMDIParent.GetAttribute("Left"))
- .Top = Integer.Parse(ndMDIParent.GetAttribute("Top"))
- .Width = Integer.Parse(ndMDIParent.GetAttribute("Width"))
- .Height = Integer.Parse(ndMDIParent.GetAttribute("Height"))
- End With
- For Each xelMDIChild As XmlElement In ndMDIParent.SelectNodes(xpath:="MDIChildren/MDIChild")
- With New frmToAdd 'MDICHild positionieren, weitere Eigenschaften festlegen
- .MdiParent = Me
- .Left = Integer.Parse(xelMDIChild.GetAttribute("Left"))
- .Top = Integer.Parse(xelMDIChild.GetAttribute("Top"))
- .Width = Integer.Parse(xelMDIChild.GetAttribute("Width"))
- .Height = Integer.Parse(xelMDIChild.GetAttribute("Height"))
- Dim ndText As XmlNode = xelMDIChild.SelectSingleNode("TextboxText")
- .TextBox1.Text = ndText.InnerText
- .Show()
- End With
- Next
- 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:
...Und dann nach einem MDIChild bestimmter Breite suchen:
Das geht mit XPath übrigens noch einfacher:
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
- Private Function AppendElement(ByVal ndParent As XmlNode, ByVal ElName As String) As XmlElement
- 'XmlElement erzeugen und in ndParent einhängen, das neue XmlElement zurückgeben
- Dim XDoc As XmlDocument
- If ndParent.NodeType = XmlNodeType.Document Then 'ndParent ist das OwnerDocument selbst
- XDoc = DirectCast(ndParent, XmlDocument)
- Else
- XDoc = ndParent.OwnerDocument
- End If
- AppendElement = XDoc.CreateElement(ElName)
- ndParent.AppendChild(AppendElement)
- End Function
SaveSettings() macht dann 3 mal Gebrauch von diesem Helferlein:
VB.NET-Quellcode
- Private Sub SaveSettings()
- Dim XDoc As New XmlDocument
- Dim xelRoot, xelMDIParent, xelMDIChildren, xelMDIChild, xelTextboxText As XmlElement
- Dim XDecl As XmlDeclaration = XDoc.CreateXmlDeclaration("1.0", "utf-8", "")
- XDoc.AppendChild(XDecl)
- Dim XComment As XmlComment = XDoc.CreateComment("Programmsettings im Xml-Format")
- XDoc.AppendChild(XComment)
- xelRoot = AppendElement(XDoc, "XmlSettings")
- xelRoot.SetAttribute("LastClosingTime", Format(Now, "Long Time"))
- xelMDIParent = AppendElement(xelRoot, "MDIParent")
- With xelMDIParent
- .SetAttribute("Left", Me.Left.ToString)
- .SetAttribute("Top", Me.Top.ToString)
- .SetAttribute("Width", Me.Width.ToString)
- .SetAttribute("Height", Me.Height.ToString)
- End With
- xelMDIChildren = AppendElement(xelMDIParent, "MDIChildren")
- For Each Frm As frmToAdd In Me.MdiChildren
- xelMDIChild = AppendElement(xelMDIChildren, "MDIChild")
- With xelMDIChild
- .SetAttribute("Left", Frm.Left.ToString)
- .SetAttribute("Top", Frm.Top.ToString)
- .SetAttribute("Width", Frm.Width.ToString)
- .SetAttribute("Height", Frm.Height.ToString)
- End With
- xelTextboxText = AppendElement(xelMDIChild, "TextboxText")
- xelTextboxText.InnerText = Frm.TextBox1.Text
- Next
- XDoc.Save(XIniFullname)
- 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
- VB-Objekt Eigenschaft Xml-Beispiel
- XmlNode ein Knoten im allgemeinen -
- XmlComment Kommentar <!--Kommentar-->
- XmlElement verschachtelungsfähig, hieraus <Elementname>...</Elementname>
- wird die hierarchische Struktur bzw.
- aufgebaut <Elementname /> (wenn leer)
- XmlAttribute einem XmlElement hinzugefügte <Elementname AttrName="AttrValue" />
- Information, wird in dessen (leeres XmlElement mit einem Attribut)
- .Attributes-Eigenschaft
- eingehängt
- XmlDocument Stamm-Objekt (= Root) wird -
- selbst auch als Knoten aufgefasst
- XmlDocumentType ein Verweis auf eine DTD-Datei, <!DOCTYPE XmlIni SYSTEM "XmlIni.dtd">
- anhand der die Gültigkeit der in
- diesem Xml-Document abgelegten
- Daten geprüft wird
- XmlText in ein Element geschachtelter Text <Elementname>Text</Elementname>
- XmlWhiteSpace Space, Linefeed etc., zur <Elementname> </Elementname>
- leserlichen Gestaltung
- XmlDeclaration Version- und Codierungs- <?xml version="1.0" encoding="utf-8"?>
- 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"
Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „ErfinderDesRades“ ()