Andreas Lindig: /XML - nur einen Teil eines Dokuments parsen

guten Tag geneigtes Forum,

ich parse ein Dokument mit dem SAX-Parser Expat in PHP. Gut, jetzt will ich nur Elemente mit bestimmter ID einlesen, aber wie?

Beispiel:

<quotings>
    <quote id="1">
        <category id="c1 c2 c3"/>
        ...
    </quote>
    <quote id="2">
        <category id="c4 c5"/>
        ...
    </quote>
    <quote id="3">
        <category id="c3 c5 c6"/>
        ...
    </quote>
</quotings>

jetzt will ich z.B. nur die Elemente 1 und 3 einlesen, weil sie die category "c3" enthalten, denn ich will alle Einträge mit der category "3" ausgeben, sonst nichts.

Jetzt soll ja der Vorteil eines SAX-Parsers gerade eben sein, daß man nicht das ganze (evtl. riesige) Dokument in den Speicher liest.

Wie geht man da praktisch vor? Ich rufe ja z.B. diese Funktion auf:

// Handler fuer Elemente ( oeffnende / schliessende Tags ) setzen
xml_set_element_handler( $parser, "start_element", "end_element" );

und frühestens in der aufgerufenen Funktion "start_element" kann ich feststellen, ob ich im Element "category" bin und ob dieses im Attribut "id" den Wert "c3" stehen hat. Bis hierhin ist aber das Oberelement "quote" schon eingelesen worden. Jetzt müsste ich doch den Vorgang bis zum vorigen "</qutoe>" wieder rückgängig machen. Oder ich lese das ganze Element komplett ein und lösche es wieder, wenn es nicht die richtige id enthält. Aber ist das denn nicht perfomancelastig? Da wird ja schon wieder das ganze Dokument geparst.

Ach ja: wenn ich z.B. 10 Elemente mit der id "c3" eingelesen habe, soll auch Schluß sein mit lustig - auch wenn das Dokument z.B. 1000 Datensätze hat. Wie stoppe ich denn den Parservorgang?

Gruß, Andreas

--
SELFFORUM - hier werden Sie geholfen,
auch in Fragen zu richtiges Deutsch
  1. Hallo,

    ich parse ein Dokument mit dem SAX-Parser Expat in PHP.

    Es gibt einfachere Möglichkeiten, sich Schmerzen zuzufügen.

    Jetzt soll ja der Vorteil eines SAX-Parsers gerade eben sein, daß man nicht das ganze (evtl. riesige) Dokument in den Speicher liest.

    (Standardfrage: Willst du wirklich deine Daten in einem XML-Dokument speichert und kein richtiges Datenbanksystem verwenden? Was du vorhast, sieht nach einer typischen Datenbankabfrage aus.)

    frühestens in der aufgerufenen Funktion "start_element" kann ich feststellen, ob ich im Element "category" bin und ob dieses im Attribut "id" den Wert "c3" stehen hat. Bis hierhin ist aber das Oberelement "quote" schon eingelesen worden.

    »Bis dahin« meines Wissens nicht. Wenn der End-Tag-Event des category-Elements gefeuert wird, steht der Parser exakt an dieser Stelle und hat danach noch nichts eingelesen.

    Jetzt müsste ich doch den Vorgang bis zum vorigen "</qutoe>" wieder rückgängig machen.

    Welchen Vorgang? Die Verarbeitung könnte so ablaufen (angenommen, category ist nicht immer das erste Element des quote-Elements):

    • Wenn ein Start-Tag-Event mit dem Tagnamen quote gefeuert wird, speichere die ID in einem temporären Array A im globalen Scope.
    • Wenn ein Start-Tag-Event mit dem Tagnamen category gefeuert wird, im Handler prüfen, ob »c3« im id-Attribut steht. Falls ja, setze den globalen Flag F.
    • Die Verarbeitung der anderen Elemente nach dem quote-Start-Tag hängt davon ab, ob sie CDATA-Inhalt haben. In dem Fall speichere den Tagnamen beim Start-Tag-Event und eventuelle Attribute in einer temporären Variable im globalen Scope, dasselbe bei einem darauffolgenden CDATA-Event. Beim End-Tag-Event werden diese Daten in den Array A übertragen.
    • Wenn en End-Tag-Event mit dem Tagnamen quote gefeuert wird, prüfe, ob der Flag F gesetzt ist. Falls ja, hänge den Array an einen Array B im globalen Scope an.

    Oder ich lese das ganze Element komplett ein und lösche es wieder, wenn es nicht die richtige id enthält. Aber ist das denn nicht perfomancelastig? Da wird ja schon wieder das ganze Dokument geparst.

    Das ganze Element wird sowieso »eingelesen«. Der Unterschied zum DOM ist, dass zu keiner Zeit mehr als der aktuelle Token sowie wahrscheinlich der unmittelbare Kontext auf der Markupebene im Speicher liegt. Also die Daten, die die Event-Handler bekommen.
    Das ganze Dokument wird sowieso geparst. Das heißt, du kannst das Parsen zwar vorzeitig abbrechen, aber logischerweise nicht verhindern, dass der Parser über Stellen im Dokument wandert, die für dich uninteressant sind, wenn danach noch Relevantes kommt.

    Ach ja: wenn ich z.B. 10 Elemente mit der id "c3" eingelesen habe, soll auch Schluß sein mit lustig - auch wenn das Dokument z.B. 1000 Datensätze hat. Wie stoppe ich denn den Parservorgang?

    Dann setze eine globale Variable, die zählt, wieviele Datensätze schon eingelesen wurden. Und führe xml_parse() immer mit kleinen Markuphäppchen aus, bis das Ende des Strings erreicht ist oder X gesuchte Datensätze eingelesen wurden.

    Mathias

    1. Hallo Mathias,

      ich parse ein Dokument mit dem SAX-Parser Expat in PHP.

      Es gibt einfachere Möglichkeiten, sich Schmerzen zuzufügen.

      hehe, welche schlägst Du vor - ich kenne mich mit S/M nicht so aus ;-)

      Jetzt soll ja der Vorteil eines SAX-Parsers gerade eben sein, daß man nicht das ganze (evtl. riesige) Dokument in den Speicher liest.

      (Standardfrage: Willst du wirklich deine Daten in einem XML-Dokument speichert

      NEIN! - ich will natürlich nicht. Aber es ist für die Zitatesammlung von SELFHTML und da muß ich mich wohl oder übel (eher letzteres ;-) an das hier gebräuchliche Format anpassen.

      Was du vorhast, sieht nach einer typischen Datenbankabfrage aus.

      find ich auch ;-) und in DB wäre es auch schon fertig.

      vielen Dank für Deine Ausführungen. Der entscheidende Denkanzatz war, die Prüfungen im end-event-Handler durchzuführen. Es funktioniert jetzt. Daß das Dokument immer komplett geparst werden muß, war mir mangels XML-Kenntnissen nicht so klar. Aber sei's drum...

      Gruß, Andreas

      --
      SELFFORUM - hier werden Sie geholfen,
      auch in Fragen zu richtiges Deutsch