bernd2k: XML parsen

Hallo,

also ich komme nicht weiter. Kann man mit VB/ ASP denn überhaupt effizient XML Dokumente parsen? Sicher, nur versteh ich es wahrscheinlich nicht.

Ich habe eine XML-Datei die wie folgt aufgebaut ist
<buecher>
  <buch>
    <autor>AAA</autor>
    <titel>BBB</titel>
  </buch>
  <buch>
   .....
</buecher>

Ich komme einfach nicht dahinter wie ich auf die Elemte zugreifen kann. In PHP ist das so easy.

Habe es bis jetzt soweit, aber da kommt zwar alles raus, was ich brauche, aber alles vollkommen unstrukturiert. Ich weiss also nicht, wann ein <buch> abgearbeitet ist und wann das naechste anfaengt.

Set rootNode = XMLDoc.documentElement

If rootNode.hasChildNodes() Then
  For Each buch in rootNode.childNodes
    response.write rootNode.nodeName & ": " & buch.Text & "<p>"
  next
end if

Ausserdem kann ich mit rootNode.nodeName nicht auf den Namen des Tags wie "autor" und "titel" zugreifen.

Wo liegt mein Verstaendnisproblem? Ich dachte die Parser sind soweit, dass mir eine Information geliefert wird, wann ein Element <buch> abgearbeitet ist.

Danke

  1. Hallo,

    um XML Dokumente zu parsen benutzt man für gewöhnlich eine entsprechende Komponente, für MS/ASP/VB wäre es beispielsweise der MSXML Parser in der jeweilig zur Verfügung stehenden Version. Der hat dann Klassen, die DOM implementieren und in deren Instanzen man XML Markup laden kann.

    Ich habe eine XML-Datei die wie folgt aufgebaut ist
    <buecher>
      <buch>
        <autor>AAA</autor>
        <titel>BBB</titel>
      </buch>
      <buch>
       .....
    </buecher>

    Ich komme einfach nicht dahinter wie ich auf die Elemte zugreifen kann. In PHP ist das so easy.

    Dafür gibt es die .ChildNodes Collection von XmlNode sowie die Methoden Select/SelectSingleNode von XmlNode

    Habe es bis jetzt soweit, aber da kommt zwar alles raus, was ich brauche, aber alles vollkommen unstrukturiert. Ich weiss also nicht, wann ein <buch> abgearbeitet ist und wann das naechste anfaengt.

    Was ist unstrukturiert? Das du eine Schleife benutzt um jedes XmlNode Object in der XmlNode.ChildNodes Collection verarbeiten zu können. <buch> ist in dem Fall ein XmlNode aus XmlDoc.DocumentElement.ChildNodes. Das DocumentElement hat den Namen "buecher". Mit jeder Schleifeniteration arbeitest du also genau ein <buch> ab. Was ist daran so schwer?

    Set rootNode = XMLDoc.documentElement

    If rootNode.hasChildNodes() Then
      For Each buch in rootNode.childNodes
        response.write rootNode.nodeName & ": " & buch.Text & "<p>"
      next
    end if

    Ausserdem kann ich mit rootNode.nodeName nicht auf den Namen des Tags wie "autor" und "titel" zugreifen.

    rootNode.NodeName zeigt auch auf das DocumentElement, <buecher>. Und nicht auf eines der Objekte "buch" aus der DocumentElement.ChildNodes Collection. "buch" selbst beinhaltet übrigens gar keinen eigenen .Text

    Wo liegt mein Verstaendnisproblem? Ich dachte die Parser sind soweit, dass mir eine Information geliefert wird, wann ein Element <buch> abgearbeitet ist.

    Die Parser selbst wohl schon, nur muss ihnen auch gesagt werden, was sie tun sollen. Wenn du den InnerText Wert <autor> haben willst, dann solltest du auch auf diesen Node zugreifen. z.b. mit

      
    Set rootNode = XMLDoc.documentElement  
    If rootNode.hasChildNodes() Then  
      For Each buch in rootNode.childNodes  
        autorNode = buch.SelectSingleNode("autor")  
           response.write autorNode.nodeName & ": " & autorNode.Text & "<p>"  
      Next  
    End If  
    
    

    Hilft dir das jetzt etwas weiter?

    Cheers,
    Frank

    1. Hallo,

      erst mal vielen Dank fuer die ausfuehrliche Antwort!

      um XML Dokumente zu parsen benutzt man für gewöhnlich eine entsprechende Komponente, für MS/ASP/VB wäre es beispielsweise der MSXML Parser in der jeweilig zur Verfügung stehenden Version. Der hat

      Okay, das hab ich und ich benutze den MSXML-Parser auch.

      Ich habe eine XML-Datei die wie folgt aufgebaut ist
      <buecher>
        <buch>
          <autor>AAA</autor>
          <titel>BBB</titel>
        </buch>
        <buch>
         .....
      </buecher>

      Ich komme einfach nicht dahinter wie ich auf die Elemte zugreifen kann. In PHP ist das so easy.

      Dafür gibt es die .ChildNodes Collection von XmlNode sowie die

      Okay, das verstehe ich auch. Mein Problem liegt vielleicht dabei, dass ueber <buecher> noch ein Element <bibliothek> liegt. Kann man denn den Einstiegspunkt des rootNode angeben? Denn mit
         Set rootNode = XMLDoc.documentElement
      greift der auf <bibliothek> zu und nicht auf <buecher>
      Ich habe mir dann etwas rekursives zusammengebaut.
      Das greift dann auf die tieferliegenden Elemnte genau so zu, wie ich es will, ABER, ich bekomme vom Parser keine Rueckmeldung/ ein Flag oder aehnliches, wann denn ein 'childNode' angearbeitet ist.
      Ich moechte bspw. jedes <buch> auf einer HTML-Seite ausgeben und nach jedem Buch ein Trennstrich ziehen.
      Bis jetzt faellt mir nur eine Konstruktion ein mitzuzaehlen, wie oft der Parser <buch> gefunden hat und wenn die Anzahl grade ist, muss er wohl </buch> gefunden haben und ich weiss, dass ich jetzt den Trennstrich ziehen kann. Das kann doch aber nicht wirklich so gedacht sein, oder? Liefert denn der Parser nichts zurueck, dass er einen Knoten abgearbeitet hat?

      Methoden Select/SelectSingleNode von XmlNode

      Hm, das muss ich mir mal anschauen

      Was ist unstrukturiert? Das du eine Schleife benutzt um jedes XmlNode Object in der XmlNode.ChildNodes Collection verarbeiten zu können. <buch> ist in dem Fall ein XmlNode aus XmlDoc.DocumentElement.ChildNodes. Das DocumentElement hat den Namen "buecher". Mit jeder Schleifeniteration arbeitest du also genau ein <buch> ab. Was ist daran so schwer?

      Eben dass ich nicht mitbekomme, wann ein <buch> 'abgearbeitet' ist.

      was sie tun sollen. Wenn du den InnerText Wert <autor> haben willst, dann solltest du auch auf diesen Node zugreifen. z.b. mit

      Set rootNode = XMLDoc.documentElement
      If rootNode.hasChildNodes() Then
        For Each buch in rootNode.childNodes
          autorNode = buch.SelectSingleNode("autor")
             response.write autorNode.nodeName & ": " & autorNode.Text & "<p>"
        Next
      End If

      Ja, das geht, wenn das nur einmal verschachtelt ist, aber ich bekomme nicht mit, wann ein <buch> fertig ist, wenn ein childNode noch mal childnodes hat. Also wie bei mir, wo ueber <buecher> noch <bibliothek> liegt.  
      Mein Versuch bis jetzt  
        
      Ich habe auch mal versucht, ob mir der Parser nur die Elemente rausholt die <buch> sind. Aber mit  
       liste = XMLDoc.GetElementsByTagName("buch")  
      bekomme ich nur den Fehler "Wrong number of arguments or invalid property assignment: 'liste'" geliefert.  
        
        
      Danke noch mal fuer deine Hilfe, evtl. habe ich ein tieferliegendes Verstaendnisproblem, da ich mir nicht vorstellen kann, dass das so schwierig sein kann, da XML ja nun wirklich ueberall genutzt wird.  
        
        
      
      
      1. Hallo,

        ein MSXML.XMLNode hat eine Collection von untergeordneten XMLNode Objekten, .ChildNodes genannt. Durch diese kann man mittels einer "for each" Schleife iterieren. Ein Element der Collection ist dann quasi abgearbeitet, wenn die Schleife zum nächsten Durchgang gelangt, also wenn man (der Entwickler) das "Next" Kommando der Schleife ausführt. Bis dahin bleibt man im Kontext des aktuellen Elements aus der Iteration. In deinem Falle Buch.

        Wenn ich dich richtig verstehe, dann solltest du vor dem NEXT einfach noch ein Response.Write "Trennstrich" & "<br/> platzieren.

        Du hast aber anscheinend generell ein Verständnisproblem mit der Benutzung des DOM Modells vom XMLDomDocument.

        Die Funktion XMLDoc.GetElementsByTagName("buch") wird evt. nicht nur ein Argument benötigen? Vielleicht gibt es sie auch gar nicht. Schau dir in deinem Entwicklungsstudio bzw. im MSDN die Objektreferenz vom MSXML Parser an.

        Eine imho recht effektive Sache zur Ermittlung aller <buch> elemente ist:

        XMLDoc.DocumentElement.SelectNodes("//buch")    das gibt dir eine XmlNodeList (iirc) zurück, die du mit einer "for each" schleife iterieren kannst. XmlNodeList beinhaltet dann XmlNodes mit dem NodeName "buch" und (wahrscheinlich) den Childnodes <autor> und <titel>. "//buch" ist dabei ein XPATH Query

        Wenn du meinst, mit PHP ist das so einfach, wie stellst du denn da fest, wann ein <buch> _abgearbeitet_ ist?

        Cheers, so long,
        Frank

        1. ein MSXML.XMLNode hat eine Collection von untergeordneten XMLNode Objekten, .ChildNodes genannt. Durch diese kann man mittels einer "for each" Schleife iterieren. Ein Element der Collection ist dann quasi abgearbeitet, wenn die Schleife zum nächsten Durchgang gelangt, also wenn man (der Entwickler) das "Next" Kommando der Schleife ausführt. Bis dahin bleibt man im Kontext des aktuellen Elements aus der Iteration. In deinem Falle Buch.

          Hm, das ist klar. Nur durch die 2fache Verschachtelung (<bibliothek> -> <buecher> -> <buch>) erhalte ich immer nur das Ende der ersten Ebene.
          Also ich weiss immer wann <buecher> abgearbeitet ist, aber nicht wann <buch> fertig ist. Ich kann mir zwar alles unterhalb von <buch> ausgeben lassen, weiss aber nie, wann ich den Trennstrich ziehen kann.

          Wenn du meinst, mit PHP ist das so einfach, wie stellst du denn da fest, wann ein <buch> _abgearbeitet_ ist?

          Bei PHP kann ich mir alle Elemente eines bestimmten Nodes in einer Liste geben lassen mit z.B.

          $domNode=$rootDomNode->get_elements_by_tagname("buecher");

          Dadurch habe ich einen Einsprungpunkt, wo keine weitere Verschachtelung mehr auftritt.

          Dann kann ich mit einer Schleife durch die Liste durchgehen und dann jedes Buch EINZELN bearbeiten.

          Aber vielen Dank fuer deine Hilfe. Werd mir das auch noch mal anschauen und deine Tipps abarbeiten.
          Aber ich denke auch eher, dass es daran liegt, dass mir verstaendnissmaessig das Konzept XML an sich nicht in den Kopf will.
          Ich persoenlich finde es schon komisch, mich erst in eine Struktur einarbeiten zu muessen, um dann erst die Daten herausholen zu koennen. Aber das tut hier ja eigentlich nichts zur Sache :-)

          1. Hallo,

            wenn du mit VB arbeitest, must du für zuweisung von Objekt Instanzen zu Variablen das Keyword SET davorschreiben:

            nicht: liste = XMLnode.Select ...

            Sondern SET liste = XMlNode.Select ...

            Nur durch die 2fache Verschachtelung (<bibliothek> -> <buecher> -> <buch>) erhalte ich immer nur das Ende der ersten Ebene.

            Dann schreibst du halt 2 Schleifen ineinander (ist zwar nicht wirklich die allgemeingültige Lösung) aber

            mit
            $domNode=$rootDomNode->get_elements_by_tagname("buecher");
            und darauffolgender Abarbeitung von $domNode in einer Schleife machst du auch nix anderes als wenn du es in VB machen würdest. Nur dass beim MSXML Parser vielleicht manche Funktionen anders heissen oder nicht vorhanden sind gegenüber PHP und das Schleifen vielleicht anders deklariert werden als du gewohnt bist. Da ne Schleife und dort ne Schleife, genau dasselbe, und darin bearbeitest du dann das Element was gerade für den Schleifendurchlauf X aktuell ist.

            Ich persoenlich finde es schon komisch,

            Wenn du es komisch findest, warum benutzt du dann XML. Zum Experimentieren, okay, aber wenns für eine konkrete Aufgabenstellung ist, schau doch mal ob nicht andere Techniken dasselbe Ergebnis liefern und dabei einfacher für dich sind. Man muss XML nicht verwenden nur weil es es gibt.

            Seitdem es MS SQL 2005 gibt, welcher nativ XML unterstützt samt XQuery usw spriessen die Lösungen dafür (wo auch dieses XML-Feature benutzt wird) quasi wie Pilze aus dem Boden, aber unter ca. 30 Systemen, die ich bislang zur Revision anschauen durfte, waren vielleicht 3 oder 4 dabei, wo die Benutzung von XML a) Sinn machte (weil sonst nix anderes möglich) and/or/xor b) sinnvoll implementiert wurde.  Typische Entwicklerkrankheit, immer das neueste und fancy-este zu nehmen :)

            Cheers,
            Frank

        2. Hallo,

          Die Funktion XMLDoc.GetElementsByTagName("buch") wird evt. nicht nur ein Argument benötigen? Vielleicht gibt es sie auch gar nicht. Schau dir in deinem Entwicklungsstudio bzw. im MSDN die Objektreferenz vom MSXML Parser an.

          Also laut http://www.topxml.com/xml_dom/getelementsbytagname.asp#P4300_94286 sollte das so gehen.

          Eine imho recht effektive Sache zur Ermittlung aller <buch> elemente ist:

          XMLDoc.DocumentElement.SelectNodes("//buch")    das gibt dir eine XmlNodeList (iirc) zurück, die du mit einer "for each" schleife iterieren kannst.

          Hab ich probiert, auch da erhalte ich als Fehler:
          "Wrong number of arguments or invalid property assignment:"

          Leider finde ich nichts, woran das liegen koennte, denn auch hier finde ich bei http://www.topxml.com/xml_dom/selectnodes.asp#P4741_114632 nichts, was auf weitere Argumente als zu uebergebende Parameter hinweist.

          Danke