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.
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
ü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.
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.
Optionen des Parsers lassen sich mit der Funktion xml_parser_set_option() und folgenden Werten setzen:
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:

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
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
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:
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.

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.
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

comelio.com


