Start
Unternehmen
Buch-Katalog
Seminare
Leserservice
Comelio-Blog
Datenbanken
SQL
MS SQL Server
Oracle
PHP

LDAP

MS SQL Server

Reflection

SAX

SQLite

Enterprise Muster

UML
C#.NET
XML Schema
XSLT
ERP / PPS / Prozesse
Business Intelligence
Server-Technologien
Software-Technologien
Technologie-Beratung
Individual-Software
Produkte

Comelio GmbH
Rellinghauser Straße 10
D-45128 Essen
Deutschland
Fon: 0201-437517-0
Fax: 0201-437517-10
info@comelio.com

Comelio GmbH
Goethestraße 34
D-13086 Berlin
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Glockengießerwall 17
D-20095 Hamburg
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Mainzer Landstraße 27-31
D-60329 Frankfurt
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Stiglmaierplatz/Dachauer Str. 37
D-80335 München
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Liebknechtstr. 33
D-70565 Stuttgart
Deutschland
info@comelio.com

Comelio GmbH
Nevinghoff 16
D-48147 Münster
Deutschland

Comelio GmbH
Friedrich - List - Platz 1
D-04103 Leipzig
Deutschland

Comelio GmbH
St. Johanner Strasse 41-43
D-66111 Saarbrücken
Deutschland

Comelio GmbH
Kaiser-Wilhem-Ring 27–29
D-50672 Köln
Deutschland

Comelio GmbH
Münsterstraße 248
D-40470 Düsseldorf
Deutschland

Comelio GmbH
Fürther Strasse
D-90429 Nürnberg
Deutschland

Comelio GmbH

Bremen
Deutschland

Comelio-Blog > PHP > SAX

XML-Verarbeitung mit PHP und SAX

SAX (Simple API for XML) zählt zu den berühmten Quasi-Standards, die weniger auf den Rückhalt einer starken Organisation wie das W3C oder die ISO vertrauen dürfen, sondern mehr auf die Popularität ihrer Webseite (http://www.saxproject.org/) hoffen. In sehr vielen Programmiersprachen ist der SAX-Parser implementiert und lässt sich auf unterschiedliche Weise ansprechen und nutzen. In einigen Systemen ist er auch nicht mehr verfügbar, weil mehr auf das DOM und XSLT – also W3C-Standards – gesetzt wird als auf proprietäre Umsetzungen von Quasi-Standards.

Die Verwendung in z.B. PHP ist relativ simpel, wenn auch im Vergleich zu anderen Möglichkeiten der XML-Verarbeitung die Anzahl benötigter Quelltextzeilen bei der Verwendung von SAX etwas umfangreicher ist. Das Kernstück des ereignisorientierten Transformationsprozesses ist eine Fallunterscheidung, in der den öffnenden und geschlossenen Tags, den Attributen und Textknoten geeignete Verarbeitungsanweisungen mit auf den Weg gegeben werden. Der Parser übersetzt dann den gesamten XML-Strom anhand der verschiedenen Fallunterscheidungen für die Ereignisse »Tag offen«, »Tag geschlossen« oder »Textknoten gefunden« in einen Ausgabestrom, der z.B. aus HTML-Tags bestehen kann.

Dieser Artikel zeigt die Verwendung des SAX-Parsers in PHP.

Kontakt

Anrede* Herr Frau
Vorname*
Nachname*
Firma
E-Mail*
Tel-Nr.
Bereich*
Freitext

PHP und XML-Verarbeitung mit SAX

SAX: Wie verarbeite ich XML-Elemente zu HTML?

Ein einfaches, aber häufiges Problem, welches bei der XML-Verarbeitung zu lösen ist, stellt die Erzeugung von HTML-Tags dar, d.h. ein XML-Dokument wird für die Speicherung von Textdaten benutzt, welche ohne große Anforderungen an die Verarbeitung in HTML ausgegeben werden sollen. Folgende Klasse erlaubt die Vorgabe von Wertepaaren in Array-Form, um die Umwandlung von XML- in HTML-Tags anzugeben, während der Parser die komplette Manipulation übernimmt.

Für die Verarbeitung von XML mit Hilfe von SAX sind folgende Funktionen nötig, welche natürlich andere Namen besitzen können. In der Klasse stellen sie gleichnamige Methoden dar.

  • startElementHandler(): reagiert darauf, dass ein geöffneter Tag im XML-Dokument gefunden wird. Die Reaktion kann aus einer Ausgabe eines HTML oder XML-Tags sowie Zeichenketten bestehen.
  • endElementHandler():reagiert darauf, dass ein geschlossener Tag im XML-Dokument gefunden wird. Die Reaktion kann aus einer Ausgabe eines HTML oder XML-Tags sowie Zeichenketten bestehen.
  • characterDataHandler(): reagiert darauf, dass Zeichendaten im XML-Dokument gefunden werden. Die Reaktion kann dabei aus der Ausgabe der entsprechenden Daten sowie weiteren Zeichenketten oder XML- sowie HTML-Tags bestehen.

Im Gegensatz zur direkten Verarbeitung von XML durch SAX entlang eines prozeduralen Programms bzw. ohne den Zusatz, dass diese Funktionen als Klassenmethoden bereitstehen, muss man das Parser-Handle als Objekt deklarieren. Dies lässt sich über xml_set_object($this->Parser, &$this); erreichen. Weil die Namen der Ereignisfunktionen als einfache Zeichenketten den Funktionen

  • xml_set_element_handler($this->Parser, startElementHandler", "endElementHandler");
  • xml_set_character_data_handler( $this->Parser, "characterDataHandler"); und sowie für die Angabe von Standardverhaltensweisen auch
  • xml_set_default_handler($this->Parser, "defaultHandler");

übergeben werden, erwartet der PHP-Parser, dass diese Funktionen nicht als Klassenmethoden, sondern als einfache PHP-Funktionen bekannt sind. Daher ist es notwendig, sie extra als Klassenmethoden bekannt zu machen.

class SAX_Parser {
  var $Parser;             // Parser-handler
  var $File;               // XML-file
  var $StartTags;          // Array for element-manipulation
  var $EndTags;            // Array for element-manipulation

 // Manipulate open tags
 function startElementHandler($XML_Parser, $Element, $Attribute){
   // Select suitable start tag
   if (isset($this->StartTags[$Element])){
     echo $this->StartTags[$Element];
   }
 }

 // Manipulate end tags
 function endElementHandler($XML_Parser, $Element){
   // Select suitable end tag
   if (isset($this->EndTags[$Element])){
     echo $this->EndTags[$Element];
   }
 }

 // Manipulate character Data (text nodes)
 function characterDataHandler($XML_Parser, $Data){
   echo $Data;
 }

 // Default behaviour of the parser
 function defaultHandler($XML_Parser, $Data){
   echo $Data;
 }

 /* Constructor method for class, main objective:
    use xml_set_object() in order to indicate that handler-
    functions can be found within the class */
 function SAX_Parser(){
   // Creates parser
   $this->Parser = xml_parser_create();

   // Indicates that parser is created within an object
   xml_set_object($this->Parser, &$this);

   /* Indicates the handler-methods for start- and end tags as
      well as character data handler method */
   xml_set_element_handler($this->Parser,
                           "startElementHandler",
                           "endElementHandler");
   xml_set_character_data_handler($this->Parser,
                                  "characterDataHandler");
   xml_set_default_handler($this->Parser, "defaultHandler");
   
   // Parser options
   xml_parser_set_option($this->Parser, XML_OPTION_CASE_FOLDING,
                         FALSE);
  }

 // Simple parsing method
 function Parse (){
   // Open XML-file
   if(!($Handle=fopen($this->File, "r"))){
     die("Datei nicht vorhanden");
   }
   // Read, traverse and manipulate file
   while ($Data = fread($Handle, 4096)){
     if (!xml_parse($this->Parser, $Data, feof($Handle))){
       die("XML Parser-Fehler");
     }
   }
   // Free parser
   xml_parser_free($this->Parser);
 }
}
Umwandlung von XML nach HTML

Für den Test der Funktionsweise der Umwandlungsklasse benötigt man zunächst eine passende XML-Datei wie die folgende:

<?xml version="1.0" encoding="ISO-8859-1"?>
<Tarifliste>
  <Tarif>
    <Name Nr="1">Frühstück</Name>
    <Gueltigkeit>
      <Datum>
        <Von>01.01.03</Von>
        <Bis>30.06.03</Bis>
      </Datum>
      <Uhrzeit>
        <Von>6</Von>
        <Bis>10</Bis>
      </Uhrzeit>
    </Gueltigkeit>
    <Preis>0,5</Preis>
  </Tarif>
...
Umzuwandelnde XML-Tabelle

Für die Verarbeitung der Elemente gibt man in zwei Arrays an, welche die auffindbaren Elemente und die „Übersetzung“ in HTML-Elemente speichern. Zusätzlich lassen sich auch Zeichenketten verwenden, die vor oder nach einem Tag oder anstelle eines solchen ausgegeben werden sollen.

// Inclusion of classes
 include("SAX_XML2HTML.php");
 
 // Create Parser
 $SAX = new SAX_Parser();
 $SAX -> File       = "tarifliste_elemente.xml";
// Manipulation of found elements
 $SAX -> StartTags = array('Tarifliste' => '<ul>',
                           'Tarif'      => '<li>',
                           'Name'       => '<b>',
                           'Preis'      => ' ',
                           'Typ'        => ' Typ: ',
                           'Nr'         => ' (Nr. ',
                           'Gueltigkeit'=> '<table border=\"1\">'
                                           .'<tr><th>Datum</th>'
                                           .'<th>Uhrzeit</th>'
                                           .'</tr><tr>',
                           'Datum'      => '<td>',
                           'Uhrzeit'    => '<td>');
 $SAX -> EndTags = array('Tarifliste'  => '</ul>',
                         'Tarif'       => '</li>',
                         'Name'        => '</b>',
                         'Preis'       => ' €',
                         'Nr'          => ') ',
                         'Gueltigkeit' => '</tr></table>',
                         'Datum'       => '</td>',
                         'Uhrzeit'     => '</td>',
                         'Von'         => ' - ');
 // Start parsing-process
 $SAX -> Parse();
Verarbeitung von XML nach HTML

Man erhält dann als Ergebnis eine HTML-Seite mit allen in der XML-Datei auffindbaren Tarifen.

Ausgabe im Browser

SAX: Wie verarbeite ich XML individuell?

XML-Dateien neigen dazu, sehr individuelle Strukturen zu haben und sperren sich dagegen manchmal gegen eine Verarbeitung, die über eine Klasse wie im Artikel-Abschnitt "SAX: Wie verarbeite ich XML-Elemente zu HTML?" angegeben wurde. Die SAX-Funktionen in PHP sind zudem in einer bestimmten Weise miteinander zu kombinieren, wobei ihr Einsatz innerhalb einer Klasse von einer prozeduralen Verwendung stark differiert. Das aktuelle Rezept zeigt daher, wie mit Hilfe der einzelnen Funktionen eine individuelle Verarbeitung über eine switch-Anweisung erfolgen kann, welche auf die unterschiedlich öffnenden und schließenden Tags reagiert und passende HTML-Tags oder Zeichenketten ausgibt.

Für die Verarbeitung von XML mit Hilfe von SAX sind folgende Funktionen nötig, welche natürlich andere Namen besitzen können. In der Klasse stellen sie gleichnamige Methoden dar.

  • startElementHandler(): reagiert darauf, dass ein geöffneter Tag im XML-Dokument gefunden wird. Die Reaktion kann aus einer Ausgabe eines HTML oder XML-Tags sowie Zeichenketten bestehen.
  • endElementHandler():reagiert darauf, dass ein geschlossener Tag im XML-Dokument gefunden wird. Die Reaktion kann aus einer Ausgabe eines HTML oder XML-Tags sowie Zeichenketten bestehen.
  • characterDataHandler(): reagiert darauf, dass Zeichendaten im XML-Dokument gefunden werden. Die Reaktion kann dabei aus der Ausgabe der entsprechenden Daten sowie weiteren Zeichenketten oder XML- sowie HTML-Tags bestehen.

Optionen des Parsers lassen sich mit der Funktion xml_parser_set_option() und folgenden Werten setzen:

  • Groß- und Kleinschreibung: xml_parser_set_option($XML_Parser, XML_OPTION_CASE_FOLDING, FALSE); Zeichensatz mit den Werten ISO-8859-1 (Standardwert), US-ASCII und UTF-8, wobei die fehlende Angabe von solchen Zeichensätzen dazu führen kann, dass deutsche Umlaute oder französische und spanische Akzente nicht im Ausgabestrom erscheinen. xml_parser_set_option($XML_Parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
  • Leerraumbehandlung mit der Angabe, ob Leeraum erhalten bleiben oder gelöscht werden soll: xml_parser_set_option($XML_Parser, XML_OPTION_SKIP_WHITE, 1);

Das entscheidende Moment der gesamten Verarbeitung liegt allerdings in den Fallunterscheidungen, welche die Namen der XML-Tags ansprechen und schließlich zu einer Ausgabe führen.

<?php
/////////////////////////////////////////////
// Individual transformation of XML into HTML
////////////////////////////////////////////

// Transformation of open tags
function startElementHandler($XML_Parser, $Element, $Attribute){
  switch ($Element){
    case "Name": echo "<h1>" . $Attribute["Nr"] . ". ";
    break;
    case "Gueltigkeit": echo "<ul>";
    break;
    case "Datum": echo "<li>";
    break;
    case "Uhrzeit": echo "<li>";
    break;
    case "Bis": echo " - ";
    break;
    case "Preis": echo "<li>Preis: <b>";
    break;
  }
}

// Transformation of close tags
function endElementHandler($XML_Parser, $Element){
  switch ($Element){
    case "Name": echo "</h1>";
    break;
    case "Datum": echo "</li>";
    break;
    case "Uhrzeit": echo " Uhr</li>";
    break;
    case "Preis": echo " €</b></li></ul>";
    break;
  }
}

// Transformation of text nodes
function characterDataHandler($XML_Parser, $Data){
 echo $Data;
}

// Default behaviour of the parser
function defaultHandler($XML_Parser, $Data){
  echo $Data;
}
// Name XML-file
$XML_File = "tarifliste_elemente.xml";
// Create parser and parser handle
$XML_Parser = xml_parser_create();

// Set handler functions for open and close tags and text nodes
xml_set_element_handler($XML_Parser, "startElementHandler",
                                     "endElementHandler");
xml_set_character_data_handler($XML_Parser,
                               "characterDataHandler");
xml_set_default_handler($XML_Parser, "defaultHandler");
// Parser options
xml_parser_set_option($XML_Parser, XML_OPTION_CASE_FOLDING,
                      FALSE);   // Case sensitivity
xml_parser_set_option($XML_Parser, XML_OPTION_TARGET_ENCODING,
                      "ISO-8859-1");   // Encoding
xml_parser_set_option($XML_Parser, XML_OPTION_SKIP_WHITE,
                      1);   // White space
                      
// Test and read file
if(!($Handle = fopen($XML_File, "r"))){
  die("File not found.");
}
while ($Daten = fread($Handle, 4096)){
  // Parse file
  if (!xml_parse($XML_Parser, $Daten, feof($Handle))){
    die("XML parser error.");
  }
}

// Free parser
xml_parser_free($XML_Parser);
?>
SAX_HTML2XMLind.php: Individuelle Transformation von XML

Als Beispiel verwendet man die XML-Datei, welche im Abschnitt "SAX: Wie verarbeite ich XML-Elemente zu HTML?" Listing tarifliste_elemente.xml angegeben wurde. Sie enthält eine Übersicht von Telefontarifen mit Gültigkeitsinformationen zu Uhrzeit und Datum sowie natürlich dem Minutenpreis. Man erhält als Ergebnis folgende Ausgabe im Browser:

Ausgabe im Browser

SAX: Wie kann ich Fehler bemerken?

Es bietet sich eine Vielzahl an Fehlern, die bei der Verwendung von XML auftreten kann. Neben den reinen Dateifehlern, welche bei jeder Verwendung von Dateien (Lese- und Schreibfehler allgemeiner Art, unpassende Zeichensätze etc.) auftreten können, können spezielle XML-Fehler entstehen. Diese lassen sich teilweise auf nicht wohlgeformte Dokumente und damit fehlende oder fehlerhafte Angaben und Strukturen im XML-Dokument zurückführen. Mit SAX lassen sich die nachfolgenden Fehler abfangen, wobei hier die Dokumentation nur einen kleinen Ausschnitt nennt, in Wirklichkeit aber mehr Fehler entdeckt werden. Nicht alle Fehlertexte entsprechen den Fehlermeldungen in der Tabelle.

Fehler 0 - 47 Fehler 48 - 94
XML_ERR_OK = 0 
XML_ERR_INTERNAL_ERROR = 1 
XML_ERR_NO_MEMORY = 2 
XML_ERR_DOCUMENT_START = 3 
XML_ERR_DOCUMENT_EMPTY = 4 
XML_ERR_DOCUMENT_END = 5 
XML_ERR_INVALID_HEX_CHARREF = 6 
XML_ERR_INVALID_DEC_CHARREF = 7 
XML_ERR_INVALID_CHARREF = 8 
XML_ERR_INVALID_CHAR = 9 
XML_ERR_CHARREF_AT_EOF = 10 
XML_ERR_CHARREF_IN_PROLOG = 11 
XML_ERR_CHARREF_IN_EPILOG = 12 
XML_ERR_CHARREF_IN_DTD = 13 
XML_ERR_ENTITYREF_AT_EOF = 14 
XML_ERR_ENTITYREF_IN_PROLOG = 15 
XML_ERR_ENTITYREF_IN_EPILOG = 16 
XML_ERR_ENTITYREF_IN_DTD = 17 
XML_ERR_PEREF_AT_EOF = 18 
XML_ERR_PEREF_IN_PROLOG = 19 
XML_ERR_PEREF_IN_EPILOG = 20 
XML_ERR_PEREF_IN_INT_SUBSET = 21 
XML_ERR_ENTITYREF_NO_NAME = 22 
XML_ERR_ENTITYREF_SEMICOL_MISSING = 23 
XML_ERR_PEREF_NO_NAME = 24 
XML_ERR_PEREF_SEMICOL_MISSING = 25 
XML_ERR_UNDECLARED_ENTITY = 26 
XML_WAR_UNDECLARED_ENTITY = 27 
XML_ERR_UNPARSED_ENTITY = 28 
XML_ERR_ENTITY_IS_EXTERNAL = 29 
XML_ERR_ENTITY_IS_PARAMETER = 30 
XML_ERR_UNKNOWN_ENCODING = 31 
XML_ERR_UNSUPPORTED_ENCODING = 32 
XML_ERR_STRING_NOT_STARTED = 33 
XML_ERR_STRING_NOT_CLOSED = 34 
XML_ERR_NS_DECL_ERROR = 35 
XML_ERR_ENTITY_NOT_STARTED = 36 
XML_ERR_ENTITY_NOT_FINISHED = 37 
XML_ERR_LT_IN_ATTRIBUTE = 38 
XML_ERR_ATTRIBUTE_NOT_STARTED = 39 
XML_ERR_ATTRIBUTE_NOT_FINISHED = 40 
XML_ERR_ATTRIBUTE_WITHOUT_VALUE = 41 
XML_ERR_ATTRIBUTE_REDEFINED = 42 
XML_ERR_LITERAL_NOT_STARTED = 43 
XML_ERR_LITERAL_NOT_FINISHED = 44 
XML_ERR_COMMENT_NOT_FINISHED = 45 
XML_ERR_PI_NOT_STARTED = 46 
XML_ERR_PI_NOT_FINISHED = 47
XML_ERR_NOTATION_NOT_STARTED = 48 
XML_ERR_NOTATION_NOT_FINISHED = 49 
XML_ERR_ATTLIST_NOT_STARTED = 50 
XML_ERR_ATTLIST_NOT_FINISHED = 51 
XML_ERR_MIXED_NOT_STARTED = 52 
XML_ERR_MIXED_NOT_FINISHED = 53 
XML_ERR_ELEMCONTENT_NOT_STARTED = 54 
XML_ERR_ELEMCONTENT_NOT_FINISHED = 55 
XML_ERR_XMLDECL_NOT_STARTED = 56 
XML_ERR_XMLDECL_NOT_FINISHED = 57 
XML_ERR_CONDSEC_NOT_STARTED = 58 
XML_ERR_CONDSEC_NOT_FINISHED = 59 
XML_ERR_EXT_SUBSET_NOT_FINISHED = 60 
XML_ERR_DOCTYPE_NOT_FINISHED = 61 
XML_ERR_MISPLACED_CDATA_END = 62 
XML_ERR_CDATA_NOT_FINISHED = 63 
XML_ERR_RESERVED_XML_NAME = 64 
XML_ERR_SPACE_REQUIRED = 65 
XML_ERR_SEPARATOR_REQUIRED = 66 
XML_ERR_NMTOKEN_REQUIRED = 67 
XML_ERR_NAME_REQUIRED = 68 
XML_ERR_PCDATA_REQUIRED = 69 
XML_ERR_URI_REQUIRED = 70 
XML_ERR_PUBID_REQUIRED = 71 
XML_ERR_LT_REQUIRED = 72 
XML_ERR_GT_REQUIRED = 73 
XML_ERR_LTSLASH_REQUIRED = 74 
XML_ERR_EQUAL_REQUIRED = 75 
XML_ERR_TAG_NAME_MISMATCH = 76 
XML_ERR_TAG_NOT_FINISHED = 77 
XML_ERR_STANDALONE_VALUE = 78 
XML_ERR_ENCODING_NAME = 79 
XML_ERR_HYPHEN_IN_COMMENT = 80 
XML_ERR_INVALID_ENCODING = 81 
XML_ERR_EXT_ENTITY_STANDALONE = 82 
XML_ERR_CONDSEC_INVALID = 83 
XML_ERR_VALUE_REQUIRED = 84 
XML_ERR_NOT_WELL_BALANCED = 85 
XML_ERR_EXTRA_CONTENT = 86 
XML_ERR_ENTITY_CHAR_ERROR = 87 
XML_ERR_ENTITY_PE_INTERNAL = 88 
XML_ERR_ENTITY_LOOP = 89 
XML_ERR_ENTITY_BOUNDARY = 90 
XML_ERR_INVALID_URI = 91 
XML_ERR_URI_FRAGMENT = 92 
XML_WAR_CATALOG_PI = 93 
XML_ERR_NO_DTD = 94 
XML_ERR_CONDSEC_INVALID_KEYWORD = 95

Folgende Methode aus der SAX_Parser-Klasse des Abschnitts "SAX: Wie verarbeite ich XML-Elemente zu HTML?" enthält die möglichen Fehlerfunktionen

  • xml_error_string() – liefert den Fehlertext
  • xml_get_current_byte_index() – liefert die Byte-Position des Parsers
  • xml_get_current_column_number() – liefert die Spaltennummer des Parsers
  • xml_get_current_line_number() – liefert die Zeilennummer des Parsers
  • xml_get_error_code() – liefert die Fehlernummer

Die Funktionen werden von folgender Methode der Reihe nach aufgerufen, sodass eine aussagekräftige Fehlermeldung entsteht. Wollte man spezielle Fehlermeldungen, die man für häufiger erachtet als andere, gesondert herausgreifen und mit einem speziellen Fehlertext versehen, so müsste man diese über die Fehlernummer identifizieren und entsprechende Texte ausgeben oder speichern.

// Simple parsing method
 function Parse (){
   // Open XML-file
   if(!($Handle=fopen($this->File, "r"))){
     die("Datei nicht vorhanden");
   }
   // Read, traverse and manipulate file
   while ($Data = fread($Handle, 4096)){
     if (!xml_parse($this->Parser, $Data, feof($Handle))){
       //die("XML Parser-Fehler");
       if ($this->Error){
         $Error_Code = xml_get_error_code($this->Parser);
         die("XML Parser-Fehler (Fehlernr. "
             .$Error_Code
             .") "
             .xml_error_string($Error_Code)
             ." bei Zeile: "
             .xml_get_current_line_number($this->Parser)
             ." | Spalte: "
             .xml_get_current_column_number($this->Parser)
             ." | Byte: "
             .xml_get_current_byte_index($this->Parser));
       }
       else {
         die("XML Parser-Fehler");
       }
     }
   }
SAX_XML2HTML.php: Bemerken von Fehlern

Um die Methode Parse() aufzurufen, benötigt man ein XML-Dokument wie im Abschnitt "SAX: Wie verarbeite ich XML-Elemente zu HTML?", welches allerdings unterschiedliche Fehler besitzen kann. Folgende Fehlermeldungen könnten ausgegeben werden:

XML Parser-Fehler (Fehlernr. 32) Unsupported encoding bei Zeile: 1 | Spalte: 41 | Byte: 42
XML Parser-Fehler (Fehlernr. 65) XML_ERR_SPACE_REQUIRED bei Zeile: 1 | Spalte: 29 | Byte: 2
XML Parser-Fehler (Fehlernr. 73) XML_ERR_GT_REQUIRED bei Zeile: 4 | Spalte: 1 | Byte: 130
XML Parser-Fehler (Fehlernr. 42) XML_ERR_ATTRIBUTE_REDEFINED bei Zeile: 4 | Spalte: 72 | Byte: 151
Ausgabe im Browser

SAX: Wie kann ich XML-Strukturen in Arrays erfassen?

Die lineare Verarbeitung von XML-Dokumenten, wie sie im Abschnitt "SAX: Wie verarbeite ich XML-Elemente zu HTML?" zu sehen war, ist nicht für jeden Einsatzbereich gleich gut. An der linearen Verarbeitung kommt man zwar weitestgehend auch bei der Übertragung von XML-Strukturen in Arrays nicht umhin, wenn man tatsächlich das gesamte Dokument verarbeiten möchte, doch es besteht immerhin die Möglichkeit, direkt auf Elemente zuzugreifen, sofern man ihren Adressierungspfad innerhalb des Arrays kennt. Folgende Klasse liefert nun eine solche Array-Struktur zurück, die dann außerhalb der Klasse weiter verarbeitet werden kann.

//////////////////////////////////////////////
// Transformation of XML into HTML via Arrays
//////////////////////////////////////////////

include("SAX_XML2HTML.php");
 
class SAX_Parser_Array {
  var $Parser;             // Parser-handler
  var $File;               // XML-file
  var $Error = FALSE;      // Switch for error recognition

 // Constructor -> creates parser
 function __construct(){
   // Creates parser
   $this->Parser = xml_parser_create();
   // Parser options
   xml_parser_set_option($this->Parser, XML_OPTION_CASE_FOLDING,
                         FALSE);
 }
 
 // Prints out error messages
 private function Get_Error($Parser){
   $Error_Code = xml_get_error_code($Parser);
   die("XML Parser-Fehler (Fehlernr. "
       .$Error_Code
       .") "
       .xml_error_string($Error_Code)
       ." bei Zeile: "
       .xml_get_current_line_number($Parser)
       ." | Spalte: "
       .xml_get_current_column_number($Parser)
       ." | Byte: "
       .xml_get_current_byte_index($Parser));
 }

 private function Parse($Switch){
   // Structures to hold data
   $Elements  = array();  // Elements and attributes
   $Frequency = array();  // Frequency array of elements
   // Open XML-file
   if(!($Handle=fopen($this->File, "r"))){
     die("Datei nicht vorhanden");
   }
   // Read, traverse and manipulate file
   while ($Data = fread($Handle, 4096)){
     if (!xml_parse_into_struct($this->Parser, $Data, $Elements,
                                $Frequency)){
       //die("XML Parser-Fehler");
       if ($this->Error){
         SAX_Parser_Array::Get_Error($this->Parser);
       }
       else {
         die("XML Parser-Fehler");
       }
     }
    }
   if ($Switch == "Elements"){
    return $Elements;
   }
   else {
    return $Frequency;
   }
 }
 
 // Parsing method for arrays
 function Parse_Into_ElementArray(){
   // Structures to hold data
   $Elements = array();
   $Elements = SAX_Parser_Array::Parse("Elements");
   return $Elements;
 }
 
 // Parsing method for arrays
 function Parse_Into_FrequencyArray(){
   // Structures to hold data
   $Frequency = array();
   $Frequency = SAX_Parser_Array::Parse("Frequency");
   return $Frequency;
 }
}
SAX_array.php: Übernahme von Daten in Arrays

Man erhält im Array $Elements die folgende Struktur zurück, die bei jeder Verarbeitung zu berücksichtigen ist:

Array
(
    [0] => Array
        (
            [tag] => Tarifliste
            [type] => open
            [level] => 1
            [value] => 
  
        )

    [1] => Array
        (
            [tag] => Tarif
            [type] => open
            [level] => 2
            [value] => 
    
        )

    [2] => Array
        (
            [tag] => Name
            [type] => complete
            [level] => 3
            [attributes] => Array
                (
                    [Nr] => 1
                )
…
Ausgabe im Browser

Die Verarbeitung der Arrays erfolgt anhand ihrer standardisierten Schlüssel. Sie wird von einer foreach-Schleife angetrieben, welche indirekt das komplette XML-Dokument sukzessive durchsuchen kann, weil jedes Element mit seinen Eigenschaften wie geöffnet, geschlossen oder komplett in Dokumentreihenfolge im Array gespeichert ist. Folgende Schlüsselwerte lassen sich verwenden:

  • Tag: Enthält den Tag-Namen.
  • Value: Enthält den Textknoten, also den Text-Inhalt eines Elements.
  • Level: Enthält die Ebene, in dem das Element gefunden wurde, ausgehend von der Ebene 1 für das Wurzelelement.
  • Type: Enthält Informationen, ob das Element geöffnet (open), geschlossen (close) oder komplett (complete) ist.
  • Attributes: Enthält ein weiteres Array mit den Attributwerten, die über ihren Namen im Array-Bezeichner zu adressieren sind.

Die Navigation durch dieses Array ist nicht einfach. Allerdings lässt sich über eine foreach-Schleife immerhin erreichen, dass jedes einzelne Array-Element – und damit auch jedes einzelne XML-Element – in Dokumentreihenfolge verarbeitet werden kann. Dies gilt auch für die einzelnen Attribute eines Elements. Über entsprechend komplexe Fallunterscheidungen bzw. geeignete Bedingungen lassen sich dann konkret einzelne Tags oder Ebenen identifzieren und verarbeiten. Im Vergleich zur Variante im Abschnitt "SAX: Wie verarbeite ich XML-Elemente zu HTML?" ist dieses Vorgehen deutlich komplizierter, weil letztlich auch wiederum Fallunterscheidungen für die Verarbeitung sorgen. Der Vorteil bei dieser Variante liegt jedoch darin, dass nicht nur auf den Namen zugegriffen werden kann, sondern bspw. auch auf die Ebene, in der das XML-Element liegt.

// Inclusion of classes
 include("SAX_Array.php");
 
 // Create Parser
 $SAX = new SAX_Parser_Array();
 $SAX -> File  = "tarifliste_elemente.xml";
 $SAX -> Error = TRUE;
 
 // Start parsing-process
 $Elements = array();
 $Elements = $SAX -> Parse_Into_ElementArray();
 $Frequency = array();
 $Frequency = $SAX -> Parse_Into_FrequencyArray();

 // 1. Process resulting arrays
 $Output = "";
 foreach($Elements as $XMLTag){
  if($XMLTag["tag"] == "Tarifliste" && $XMLTag["type"] == "open"){
    $Output .= "<ul>";
  }
  if($XMLTag["tag"] == "Tarif" && $XMLTag["type"] == "open"){
    $Output .= "<li>";
  }
  if($XMLTag["tag"] == "Name" && $XMLTag["type"] == "complete"){
    $Output .= $XMLTag["attributes"]["Nr"]
              .": "
              .$XMLTag["value"];
  }
  if($XMLTag["tag"] == "Tarif" && $XMLTag["type"] == "close"){
    $Output .= "</li>";
  }
  if($XMLTag["tag"] == "Tarifliste" && $XMLTag["type"] == "close"){
    $Output .= "</ul>";
    }
 }
 echo $Output
      ."\n";
SAX_array_test.php: Verwendung von Array-Strukturen

Man erhält im Browser eine einfache Liste der Tarife.

Ausgabe im Browser

SAX: Wie erzeuge ich kommagetrennte Werte?

Für die einfache Verwendung von Daten in Datenbanken oder in Tabellenkalkulationsprogrammen sind kommagetrennte Werte bzw. überhaupt Trennzeichen getrennte Werte weiterhin eine nützliche Technologie. Nichts ist einfacher, als solche Dateien direkt aus XML zu erzeugen. Es bietet zwei grundsätzlich unterschiedliche Vorgehensweisen, um „CSV-Daten“ zu speichern, wobei besonders die gute Lesbarkeit im Vergleich zu herkömmlichen CSV-Daten, die mit der folgenden Klasser erzeugt werden sollen, zu bemerken ist. Der Vorteil, XML für die Speicherung solcher sehr flacher Datenstrukturen zu verwenden, liegt darin, dass die Zuordnung von Werten zur ihren Feldnamen sehr viel einfacher möglich ist als bei herkömmlichen CSV-Daten. Diese erlauben nur eine Zuordnung über ihre Position, nicht aber durch eine konkrete und gut lesbare Auszeichnung im Text. Sind dann auch noch die gespeicherten Werte von unterschiedlicher Länge, entsteht das Problem, dass die Spalten sehr flattern und die Dateien nur sehr schwer lesbar sind. Vorteil solcher Dateien ist dagegen ihre überaus geringe Speichergröße, weil die Trennzeichen prozentual nur sehr wenig Speicherplatz verbrauchen. Dies ist ja bei XML-Dateien gerade ganz anders, obwohl sie auch hier den Vorteil einer höheren Lesbarkeit bieten.

Folgende Varianten der Speicherung sind vorhanden:
Attribut-orientierte Darstellung
Unter einem gemeinsamen Wurzelelement stellen die verschiedenen Kind-Elemente die einzelnen Datenreihen dar. Die Felder werden dabei durch die Attribute jedes dieser Kind-Elemente abgebildet. Sofern die Reihen nicht zu lang werden, wenn viele Attribute zum Einsatz kommen oder die gespeicherten Werte sehr lang sind, ergibt sich eine sehr gut lesbare Datei. Sie ist komprimiert, jeder Zeile im Dokument, auf dem Papier oder auf dem Bildschirm entspricht einer Datenreihe, und sollten sogar die einzelnen Werte nicht allzu unterschiedlich lang sein, dann entstehen weitestgehend sogar Spalten im Schriftbild. Dies ist zwar nicht notwendigerweise korrekt eingerückt, aber insgesamt können Dateien mit hoher Lesbarkeit entstehen.
Element-orientierte Darstellung
Unter einem gemeinsamen Wurzelelement stellen die verschiedenen Kind-Elemente die einzelnen Datenreihen dar. Die Felder werden dabei durch weitere Kind-Elemente dieser Kind-Elemente abgebildet. Bei sehr vielen „Spalten“ bzw. Kind-Elementen oder auch sehr unterschiedlich langen Feldinhalten bietet dieses Format eine große Lesbarkeit, weil in der resultierenden Datei jedes Feld in einer einzelnen Reihe untergebracht ist.

Die folgende Klasse erlaubt die Verarbeitung beider Formate, wobei natürlich auch die Angabe möglich ist, welches Trennzeichen zum Einsatz kommen und ob eine Titelzeile ausgegeben werden soll. Sie greift dabei auf die SAX_Parser_Array-Klasse zurück, welche das übergebene XML-Dokument in eine Array-Struktur überführt, welche dann mit geeigneten foreach-Schleifen untersucht und verarbeitet werden kann.

//////////////////////////////////////////////
// Transformation of XML into CSV via Arrays
//////////////////////////////////////////////
include ("SAX_array.php");

class SAX_Parser_CSV {
  var $Parser;             // Parser-handler
  var $Input_File;         // XML-file
  var $Output_File = "result.csv";        // XML-file
  var $Error = FALSE;      // Switch for error recognition
  var $Elements;           // Data in array-form
  var $Separator = ",";    // Separator of data
  var $Heading   = TRUE;   // Output of columns
  var $Display   = FALSE;  // Display of result on screen
  
 // Constructor -> creates parser
 function __construct($File){
   // Create Parser
   $SAX = new SAX_Parser_Array();
   $this->Input_File = $File;
   $SAX -> File  = $this->Input_File;
   $SAX -> Error = $this->Error;

   // Start parsing-process
   $Elements = array();
   $this->Elements = $SAX -> Parse_Into_ElementArray();
 }

 // Separates fields and rows
 private function Separate_Data($Row, $i){
   $Separator = "";
   if (count($Row) == $i){
     $Separator =  "\n";
   }
   else {
     $Separator =  $this->Separator;
   }
   return $Separator;
 }
 
 // Prints file
 private function Print_File($Output){
   // Prepare output file
   if (! $FileHandle = fopen($this->Output_File, "w")){
     die ("File could not be created.");
   }
   // Write output file
   if (! fwrite($FileHandle, $Output)){
     die ("Could not write data.");
   }
 }

 // Output and display of result
 private function Save_File($Output){
   // Save to file
   SAX_Parser_CSV::Print_File($Output);
   // Display on screen
   if ($this->Display) echo $Output;
 }

 // Parses element-oriented XML to CSV
 function Parse_Elements2CSV () { //print_r($this->Elements);
  $Output = "";

  // Output heading
  $Tags[0] = array();  // All element names
  // Collects all tag names on third level
  foreach ($this->Elements as $Row){
    if ($this->Heading && $Row["level"] == 3){
      $Tags[0][] = $Row["tag"];
    }
  }
  // Collects all different element names
  $Tags[1] = array(); // All different element names
  $Tags[1] = array_unique($Tags[0]);
  $i = 0;
  foreach ($Tags[1] as $Value){
    $Output .= $Value;
    $i++;
    // Output of separator
    if (count($Tags[1]) == $i){
      $Output .= "\n";
    }
    else {
      $Output .= $this->Separator;
    }
  }
  // Output data
  foreach ($this->Elements as $Row){
    $i = 0;
    // Traverse elements (rows)
    if ($Row["level"] == 2 && $Row["type"] == "close"){
      $Output .= "\n";
    }
    // Traverse attributes (fields)
    if($Row["level"] == 3){
      $Output .= $Row["value"]
                .$this->Separator;
    }
  }
  $Output = str_replace($this->Separator."\n", "\n", $Output);
  SAX_Parser_CSV::Save_File($Output);
 }

 // Parses attribute-oriented XML to CSV
 function Parse_Attributes2CSV () {
  $Output = "";
  // Output heading
  if ($this->Heading){
    $i = 0;
    // Traverse attributes (fields and their names)
    foreach ($this->Elements[1]["attributes"] as $Key => $Value){
      $Output .=  $Key;
      $i++;
      $Output .= SAX_Parser_CSV::Separate_Data
                ($this->Elements[1]["attributes"], $i);
    }
  }
  // Output data
  foreach ($this->Elements as $Row){
    $i = 0;
    // Traverse elements (rows)
    if ($Row["level"] == 2){
      // Traverse attributes (fields)
      foreach ($Row["attributes"] as $Field){
        $Output .=  $Field;
        $i++;
        $Output .= SAX_Parser_CSV::Separate_Data($Row, $i);
      }
    }
  }
  SAX_Parser_CSV::Save_File($Output);
 }
}
SAX_CSV.php: Umwandlung von XML in CSV-Dateien

Beim Aufruf ist dem Konstruktor der Dateiname mitzugeben, welcher für die Initialisierung und den aufgerufenen Parser benutzt wird, um die Array-Struktur zu erzeugen, welche zu untersuchen ist. Es wird im Beispiel zum eine Datei mit Kind-Elementen als Datenfeldern und zum anderen eine Datei mit Attributen als Datenfeldern eingesetzt.

// Inclusion of classes
 include("SAX_CSV.php");
 
 // Generate CSV from a attribute-oriented XML-file
 $SAX = new SAX_Parser_CSV("CSV_attributes.xml");
 $SAX -> Error = TRUE;
 $SAX ->Separator = ";";
 $SAX -> Output_File = "CSV_attributes.csv";
 $SAX -> Parse_Attributes2CSV();
 
 // Generate CSV from a element-oriented XML-file
 $SAX = new SAX_Parser_CSV("CSV_elements.xml");
 $SAX -> Error = TRUE;
 $SAX -> Output_File = "CSV_elements.csv";
 $SAX ->Separator = "#";
 $SAX -> Parse_Elements2CSV();
SAX_CSV_test.php: Umwandlung von XML-Dateien in CSV-Daten

Die erste XML-Datei ist element-orientiert und speichert die einzelnen Reihen in einem Eltern-Element, welches für die verschiedenen Spalten bzw. Felder Kind-Elemente enthält.

<?xml version="1.0" encoding="ISO-8859-1"?>
<Tarifliste>
  <Tarif>
    <Name>Frühstück</Name>
    <Preis>0,5</Preis>
    <Nr>1</Nr>
    <Typ>p</Typ>
  </Tarif>
  <Tarif>
    <Name>Mittagspause</Name>
    <Preis>1</Preis>
    <Nr>2</Nr>
    <Typ>p</Typ>
  </Tarif>
…
CSV_elements.xml: Element-orientierte XML-Datei

Die zweite XML-Datei ist attribut-orientiert und speichert die einzelnen Reihen in einem Eltern-Element, welches für die verschiedenen Spalten bzw. Felder Attribute enthält.

<?xml version="1.0" encoding="ISO-8859-1"?>
<Tarifliste>
  <Tarif Name="Frühstück" Preis="0,5" Nr="1" Typ="p"/>
  <Tarif Name="Mittagspause" Preis="1" Nr="2" Typ="p"/>
  <Tarif Name="Abendessen" Preis="1" Nr="3" Typ="p"/>
  <Tarif Name="Mondschein1" Preis="0,5" Nr="4" Typ="p"/>
  <Tarif Name="Mondschein2" Preis="0,5" Nr="5" Typ="p"/>
…
CSV_attributes.xml: Attribut-orientierte XML-Datei

Man erhält entweder eine Excel-Datei bzw. eine Datei mit Kommata als Trennungszeichen oder eine Datei mit Rautenzeichen als Trennern. Sie lassen sich dann in Tabellenkalkulationsprogrammen direkt (sofern Kommata verwandt werden) oder durch Datenimport unter Angabe des Trennzeichens weiter verwenden.

Ebenso erhält man zwei passende Textdateien mit ihren unterschiedlichen Trennzeichen, die in jedem einfachen Texteditor zu betrachten sind.

Name;Preis;Nr;Typ
Frühstück;0,5;1;p
Mittagspause;1;2;p
Abendessen;1;3;p
Mondschein1;0,5;4;p
CSV_attributes.csv

Name#Preis#Nr#Typ
Frühstück#0,5#1#p
Mittagspause#1#2#p
Abendessen#1#3#p
Mondschein1#0,5#4#p
Mondschein2#0,5#5#p
Extern#1,5#6#ep
CSV_elements.csv

Ausgabe im Browser
    Comelio GmbH PHP: XML parsen mit SAX LDAP PHP SQL SQLJ XML MySQL Tutorial Handbuch Anleitung Wolfsburg Ol Freiburg Andernach Erlangen Leipzig Bonn Lübeck Magdeburg Würzuburg Heidelberg Köln Kiel Hamburg Koblenz Ludwigshafen Koblenz Stuttgart Kassel Bochum Göttingen Zwickau Bremen Frankfurt Mannheim Hannover München Berlin Aachen Ingolstadt RügenComelio GmbH PHP: XML parsen mit SAX LDAP PHP SQL SQLJ XML MySQL Tutorial Handbuch Anleitung Wolfsburg Ol Freiburg Andernach Erlangen Leipzig Bonn Lübeck Magdeburg Würzuburg Heidelberg Köln Kiel Hamburg Koblenz Ludwigshafen Koblenz Stuttgart Kassel Bochum Göttingen Zwickau Bremen Frankfurt Mannheim Hannover München Berlin Aachen Ingolstadt RügenComelio GmbH PHP: XML parsen mit SAX LDAP PHP SQL SQLJ XML MySQL Tutorial Handbuch Anleitung Wolfsburg Ol Freiburg Andernach Erlangen Leipzig Bonn Lübeck Magdeburg Würzuburg Heidelberg Köln Kiel Hamburg Koblenz Ludwigshafen Koblenz Stuttgart Kassel Bochum Göttingen Zwickau Bremen Frankfurt Mannheim Hannover München Berlin Aachen Ingolstadt RügenComelio GmbH PHP: XML parsen mit SAX LDAP PHP SQL SQLJ XML MySQL Tutorial Handbuch Anleitung Wolfsburg Ol Freiburg Andernach Erlangen Leipzig Bonn Lübeck Magdeburg Würzuburg Heidelberg Köln Kiel Hamburg Koblenz Ludwigshafen Koblenz Stuttgart Kassel Bochum Göttingen Zwickau Bremen Frankfurt Mannheim Hannover München Berlin Aachen Ingolstadt RügenComelio GmbH PHP: XML parsen mit SAX LDAP PHP SQL SQLJ XML MySQL Tutorial Handbuch Anleitung Wolfsburg Ol Freiburg Andernach Erlangen Leipzig Bonn Lübeck Magdeburg Würzuburg Heidelberg Köln Kiel Hamburg Koblenz Ludwigshafen Koblenz Stuttgart Kassel Bochum Göttingen Zwickau Bremen Frankfurt Mannheim Hannover München Berlin Aachen Ingolstadt RügenComelio GmbH PHP: XML parsen mit SAX LDAP PHP SQL SQLJ XML MySQL Tutorial Handbuch Anleitung Wolfsburg Ol Freiburg Andernach Erlangen Leipzig Bonn Lübeck Magdeburg Würzuburg Heidelberg Köln Kiel Hamburg Koblenz Ludwigshafen Koblenz Stuttgart Kassel Bochum Göttingen Zwickau Bremen Frankfurt Mannheim Hannover München Berlin Aachen Ingolstadt RügenComelio GmbH PHP: XML parsen mit SAX LDAP PHP SQL SQLJ XML MySQL Tutorial Handbuch Anleitung Wolfsburg Ol Freiburg Andernach Erlangen Leipzig Bonn Lübeck Magdeburg Würzuburg Heidelberg Köln Kiel Hamburg Koblenz Ludwigshafen Koblenz Stuttgart Kassel Bochum Göttingen Zwickau Bremen Frankfurt Mannheim Hannover München Berlin Aachen Ingolstadt RügenComelio GmbH PHP: XML parsen mit SAX LDAP PHP SQL SQLJ XML MySQL Tutorial Handbuch Anleitung Wolfsburg Ol Freiburg Andernach Erlangen Leipzig Bonn Lübeck Magdeburg Würzuburg Heidelberg Köln Kiel Hamburg Koblenz Ludwigshafen Koblenz Stuttgart Kassel Bochum Göttingen Zwickau Bremen Frankfurt Mannheim Hannover München Berlin Aachen Ingolstadt RügenComelio GmbH PHP: XML parsen mit SAX LDAP PHP SQL SQLJ XML MySQL Tutorial Handbuch Anleitung Wolfsburg Ol Freiburg Andernach Erlangen Leipzig Bonn Lübeck Magdeburg Würzuburg Heidelberg Köln Kiel Hamburg Koblenz Ludwigshafen Koblenz Stuttgart Kassel Bochum Göttingen Zwickau Bremen Frankfurt Mannheim Hannover München Berlin Aachen Ingolstadt Rügen
Seminare