Xml verarbeiten mit Intellisense (Schema und XDocument)

    • VB.NET
    • .NET (FX) 4.0

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

      Xml verarbeiten mit Intellisense (Schema und XDocument)

      Neuerlich erst verstand ich ein ungewöhnliches SprachFeature von Vb.Net-2010: die XML-Achseneigenschaften (Visual Basic) (folge dem Link)
      Damit kann man direkt im VB-Code Xml-Tags mit <> spezifizieren und Xml-Attribute mit @ (und noch paar annere Sachen mehr).
      Auch kann man mittels Imports-Anweisung dem Compiler das Schema der Xml-Datei bekannt machen.
      Intellisense hilft uns dann beim Erstellen differenzierter Abfragen auch komplexester Xml-Dateien.
      Dadurch rückt Xml-Datenverarbeitung ein Stück weit an typisierte Datenverarbeitung heran, wie sie etwa das typisierte Dataset bereitstellt. (Es ist zwar immer noch nur ein Datentyp: String, aber immerhin schlägt Intellisense nun sinnvolle Auswahlen an Strings vor)

      Hab ich malwieder ein Filmchen zu gemacht, denn wie einfach es ist, wenn Intellisense hilft, muss man gesehen haben:
      - Direkt-Link
      (Anmerkung: Im Film der Bildausschnitt zeigt leider vom Menü Xml -> Schema erstellen nur letzteres, aber das Xml-HauptMenü sieht man gleich, wenn man eine XmlDatei im Editor öffnet)

      Anmerkungen zur Abfrage-Syntax
      (Beachte: untige Zeilen #14 - #22 sind austauschbare Varianten die Verschiedenes abfragen - natürlich wird nur das erste Return (#14) tatsächlich ausgeführt)

      VB.NET-Quellcode

      1. Imports <xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      2. Public Class frmXDocDemo
      3. Public Sub New()
      4. InitializeComponent()
      5. ListBox1.DataSource = LoadData.ToList
      6. End Sub
      7. Private Function LoadData() As IEnumerable(Of String)
      8. Dim xdoc = XDocument.Load("..\..\XDocDemo.xml")
      9. Dim ref = xdoc.<Project>.<ItemGroup>.<Reference>(1)
      10. ref.@Include = "nee, dieses nich!" 'schreibzugriff aufs Include-Attribut des 2. gefundenen <Reference> - XElementes
      11. Return From xel In xdoc.<Project>.<ItemGroup>.<Reference> Select xel.@Include 'alle includierten References
      12. Return From xel In xdoc.<Project>.<ItemGroup>.<Compile> Select xel.@Include 'alle includierten Compile-Dateien
      13. Return From xel In xdoc.<Project>.<ItemGroup>.Elements Select xel.@Include 'alle includierten Dateien überhaupt
      14. Return From xel In xdoc.Descendants Where xel.@Include IsNot Nothing Select xel.@Include 'noch unspezifischer: alles includierte überhaupt
      15. Return From xel In xdoc.<Project>.<ItemGroup>.<Compile> Where xel.<AutoGen>.Value = "True" Select xel.@Include 'alle generierten Dateien
      16. Return From xel In xdoc.<Project>.<ItemGroup>.<EmbeddedResource>.<Generator> Select xel.Value 'EmbeddedResource-Generatoren
      17. Return From xel In xdoc.<Project>.<ItemGroup>.<None>.<Generator> Select xel.Value 'andere Generatoren
      18. Return From xel In xdoc.<Project>.<ItemGroup>...<Generator> Select xel.Value 'alle Generatoren
      19. Return New String() {}
      20. End Function
      21. End Class
      Man kann sich die XTags innerhalb der Query als hintereinander-geschaltete Filter vorstellen, also xdoc.<Project>.<ItemGroup>.<Reference> filtert aus allen <Project>-Knoten (es gibt nur einen) alle <ItemGroup>s, und von denen alle <Reference>s
      Das heißt es selektiert genau 3 Ebenen tief.
      Ich kann auch Ebenen überspringen: xdoc.<Project>...<Reference>
      Das würde alle <Reference>s überhaupt selektieren, hauptsache, sie liegen irgendwo unter dem <Project>-Knoten.
      (Bringt in diesem Sample keinen Unterschied, denn inne Beispiel-Xml sind die <Reference>s alle in eine <Itemgroup> gepackt)

      Aber die Ebenen-Logik der Filterung versteht man vlt. anhand dieses Xml-Ausschnitts:

      XML-Quellcode

      1. <ItemGroup>
      2. <EmbeddedResource Include="frmXDocDemo.resx">
      3. <SubType>Designer</SubType>
      4. <DependentUpon>frmXDocDemo.vb</DependentUpon>
      5. </EmbeddedResource>
      6. <EmbeddedResource Include="My Project\Resources.resx">
      7. <Generator>VbMyResourcesResXFileCodeGenerator</Generator>
      8. <LastGenOutput>Resources.Designer.vb</LastGenOutput>
      9. <CustomToolNamespace>My.Resources</CustomToolNamespace>
      10. <SubType>Designer</SubType>
      11. </EmbeddedResource>
      12. </ItemGroup>
      13. <ItemGroup>
      14. <None Include="app.config" />
      15. <None Include="My Project\Application.myapp">
      16. <Generator>MyApplicationCodeGenerator</Generator>
      17. <LastGenOutput>Application.Designer.vb</LastGenOutput>
      18. </None>
      19. <None Include="My Project\Settings.settings">
      20. <Generator>SettingsSingleFileGenerator</Generator>
      21. <CustomToolNamespace>My</CustomToolNamespace>
      22. <LastGenOutput>Settings.Designer.vb</LastGenOutput>
      23. </None>
      24. </ItemGroup>
      Hier taucht sowohl unter <ItemGroup>.<EmbeddedResource> als auch unter <ItemGroup>.<None> der Tag <Generator> auf.
      Daher können wir wie folgt abfragen:

      Visual Basic-Quellcode

      1. Return From xel In xdoc.<Project>.<ItemGroup>.<EmbeddedResource>.<Generator> Select xel.Value 'EmbeddedResource-Generatoren
      2. Return From xel In xdoc.<Project>.<ItemGroup>.<None>.<Generator> Select xel.Value 'andere Generatoren
      3. Return From xel In xdoc.<Project>.<ItemGroup>...<Generator> Select xel.Value 'alle Generatoren
      Beachte auch die annere selectete Property: Weiter oben wurde das xel.@Include-Attribut selected - hier jetzt das xel.Value
      Dateien
      • XDocDemo.zip

        (16,85 kB, 362 mal heruntergeladen, zuletzt: )

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

      Xml-Achsen-Eigenschaften unter der Haube

      Die Xml-Achsen-Syntax ist (wie so vieles in .Net) sog. "syntaktischer Zucker", also eine Syntax-Überformung der Aufrufe ganz gewöhnlicher Methoden, zum Teil Extension-Methods (aber Extensions sind ja selbst "syntaktischer Zucker").
      ich zeige mal zwei in Xml-Achsen-Notation geschriebene Abfragen (zeilen #8, #13), und jeweils darunter die Übersetzung in herkömmliche Notation:

      VB.NET-Quellcode

      1. Imports <xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      2. Private Function LoadData() As IEnumerable(Of String)
      3. Dim xdoc = XDocument.Load("..\..\XDocDemo.xml")
      4. Dim ns = xdoc.Root.Name.Namespace.NamespaceName 'NamespaceName ermitteln statt xmlns-Import
      5. 'alle includierten References
      6. Return From xel In xdoc.<Project>.<ItemGroup>.<Reference> Select xel.@Include
      7. Return From xel In xdoc.Elements(XName.Get("Project", ns)).Elements(XName.Get("ItemGroup", ns)) _
      8. .Elements(XName.Get("Reference", ns)) Select CType(xel.Attribute("Include"), String)
      9. 'alle Generatoren
      10. Return From xel In xdoc.<Project>.<ItemGroup>...<Generator> Select xel.Value
      11. Return From xel In xdoc.Elements(XName.Get("Project", ns)).Elements(XName.Get("ItemGroup", ns)) _
      12. .Descendants(XName.Get("Generator", ns)) Select xel.Value
      13. End Function


      Es geht um die Xml-Achsen-Operatoren <>, ., ..., @
      • <> entspricht XName.Get(String, String) As XName
        erstellt einen XName, ggfs. unter Berücksichtigung des importierten Xml-Namespaces. im konventionellen Aufruf muss der XName.Get()-Methode zusätzlich der NamespaceName mitgegeben werden (s. Code).

      • . entspricht XContainer.Elements(XName) As IEnumerable(Of XElement)
        ruft eine gefilterte Liste der direkten Kind-Elemente ab. Eine gleichnamige Extension erstellt die gefilterte Liste sogar aus den Kind-Elementen einer Liste von XElementen ab.

      • ... entspricht XContainer.Descendants(XName) As IEnumerable(Of XElement) (s. zeile #15)
        ruft eine gefilterte Liste der Kind- und Kindeskind-Elemente ab. Eine gleichnamige Extension ruft sie auch von einer Liste von XElementen ab.

      • @ entspricht CType(XElement.Attribute(String), String) (genau genommen Microsoft.VisualBasic.CompilerServices.InternalXmlHelper.AttributeValue(XElement, XName) As String)
        ruft den Wert eines Attributes ab - falls das Attribut nicht existiert: Nothing.
        Eine Überladung davon liefert von einer Liste von XElementen den Wert des ersten gefundenen XElementes - oder Nothing


      Zusammenfassung
      Die XmlAchsen-Eigenschaften im Zusammenspiel mit XDocument und Konsorten (aus dem System.Xml.Linq-Namespace) sind wesentlich besser leserlich als die konventionelle Notation.
      Vor allem die Intellisense-Unterstützung ist anders gar nicht verfügbar.

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