simplexml vs. SOAP xml
Nina
- php
Hi,
habe eine eigentlich valide xml Datei, die im Folgenden gekürzt zu sehen ist:
<?xml version="1.0" encoding="UTF-8" ?>
<SOAP:Envelope xmlns:SOAP="http://www.w3.org/2003/05/soap-envelope" >
<SOAP:Body >
<site><id>77</id>
<english_name><![CDATA[test]]></english_name>
<name><![CDATA[Deutschland]]></name>
<areas><area><id>20043</id>
<english_name><![CDATA[aachen]]></english_name>
<name><![CDATA[Aachen]]></name>
<language_code>de</language_code>
<time_zone><![CDATA[Central European Time]]></time_zone>
<map_enabled>true</map_enabled>
<currency_id>7</currency_id>
<currency_name><![CDATA[EUR]]></currency_name>
<sub_areas><sub_area><id>1103654</id>
<english_name><![CDATA[aachen]]></english_name>
<name><![CDATA[Aachen]]></name>
</sub_area>
<sub_area><id>1103793</id>
<english_name><![CDATA[alsdorf]]></english_name>
<name><![CDATA[Alsdorf]]></name>
</sub_area>
<sub_area><id>1103728</id>
<english_name><![CDATA[eschweiler]]></english_name>
<name><![CDATA[Eschweiler]]></name>
</sub_area>
</sub_areas>
</area>
</areas>
</site>
</SOAP:Body>
</SOAP:Envelope>
Diese würde ich liebend gern mit simplexml verabeiten, da es wirklich sehr einfach ist, damit umzugehen :-)
Leider passt irgendetwas an der xml nich, sodass simplexml nur folgendes zurückgibt:
SimpleXMLElement Object ( )
Was kann ich tun?
DANKE
echo $begrüßung;
<SOAP:Envelope xmlns:SOAP="http://www.w3.org/2003/05/soap-envelope" >
<SOAP:Body >
Leider passt irgendetwas an der xml nich, sodass simplexml nur folgendes zurückgibt: SimpleXMLElement Object ( )Was kann ich tun?
Du solltest den Namespace nicht ignorieren. Vergleiche mit jenem Forumsfädchen: </archiv/2007/10/t160998/#m1047347>.
echo "$verabschiedung $name";
Hi,
mache ich etwas falsch oder funktioniert es mit namespaces auch nicht?
<?
function display($in) {
$xml = simplexml_load_file($in);
print_r($xml);
$manifest = $xml->children('http://www.w3.org/2003/05/soap-envelope');
print_r($manifest);
}
echo display('regionen.xml');
?>
echo $begrüßung;
mache ich etwas falsch oder funktioniert es mit namespaces auch nicht?
<?
function display($in) {
$xml = simplexml_load_file($in);
print_r($xml);
$manifest = $xml->children('http://www.w3.org/2003/05/soap-envelope');
print_r($manifest);
}
echo display('regionen.xml');
?>
Da du nicht beschreibst, was du erwartest und was stattdessen bei dir passiert, kann ich dir nicht sagen, was du falsch machst. Bei mir steht in $manifest der SimpleXMLElement für SOAP:Body. Dessen Kind, das du in dem angegebenen Codestück aber nicht abfragst, ist <site>, und dessen Kinder ...
Vielleicht irritiert dich auch SimpleXML etwas. Das ist normal, denn diese Erweiterung arbeitet viel mit PHPs OOP-Magie namens Overloading. Da werden manche Eigenschaften eines Objekts erst bei einem Zugriff darauf berechnet, werden also beim print_r() und ähnlichem nicht gleich angezeigt.
echo "$verabschiedung $name";
Da du nicht beschreibst, was du erwartest und was stattdessen bei dir passiert, kann ich dir nicht sagen, was du falsch machst.
Ich suche eine Art Array, der die Daten der xml Datei representiert.
Ähnlich wie $data[site][id]=77; $data[site][english_name]=test; ...
Irgendwo sollten auch die areays darin zu finden sein usw.
Stattdessen sehe ich nur leere Objekte.
Bei mir steht in $manifest der SimpleXMLElement für SOAP:Body. Dessen Kind, das du in dem angegebenen Codestück aber nicht abfragst, ist <site>, und dessen Kinder ...
Wie komme ich an dieses ran?
Vielleicht irritiert dich auch SimpleXML etwas. Das ist normal, denn diese Erweiterung arbeitet viel mit PHPs OOP-Magie namens Overloading. Da werden manche Eigenschaften eines Objekts erst bei einem Zugriff darauf berechnet, werden also beim print_r() und ähnlichem nicht gleich angezeigt.
Gab es da nicht eine Option, womit man diese alle auf einen Schlag (zum Debuggen und Testen) aufrufen lassen kann? So wie es jetzt ist, finde ich diese Daten jedenfalls nicht.
echo $begrüßung;
Da du nicht beschreibst, was du erwartest und was stattdessen bei dir passiert, kann ich dir nicht sagen, was du falsch machst.
Ich suche eine Art Array, der die Daten der xml Datei representiert.
Ähnlich wie $data[site][id]=77; $data[site][english_name]=test; ...
Irgendwo sollten auch die areays darin zu finden sein usw.
Stattdessen sehe ich nur leere Objekte.
Ja, das ist das Problem bei der Magie in SimpleXML, besonders wenn dann noch Namespaces mitspielen.
Bei mir steht in $manifest der SimpleXMLElement für SOAP:Body. Dessen Kind, das du in dem angegebenen Codestück aber nicht abfragst, ist <site>, und dessen Kinder ...
Wie komme ich an dieses ran?
An das Kind von SOAP:Body kommst du genauso wie an das Kind von SOAP:Envelope, mit ->children(). Diesmal allerdings ohne Namespace-Angabe. Warum das so ist, weiß ich auch nicht, so tief stecke ich der SimpleXML-Problematik nicht drin.
Ab jetzt kannst du weiter wie auf einfache Eigenschaften zugreifen. Beispielsweise liefert ->site->id den Wert 77.
Vielleicht irritiert dich auch SimpleXML etwas. Das ist normal, denn diese Erweiterung arbeitet viel mit PHPs OOP-Magie namens Overloading. Da werden manche Eigenschaften eines Objekts erst bei einem Zugriff darauf berechnet, werden also beim print_r() und ähnlichem nicht gleich angezeigt.
Gab es da nicht eine Option, womit man diese alle auf einen Schlag (zum Debuggen und Testen) aufrufen lassen kann? So wie es jetzt ist, finde ich diese Daten jedenfalls nicht.
Nein, weil dann SimpleXML nicht weiß, welchen Namespace du gern verwenden willst. Es müsste bei der Anzeige einerseits irgendwie die Namespaces mit anzeigen, das aber andererseits mit den vorgegebenen Möglichkeiten und der üblichen Struktur von print_r(), und obendrin sollten die Daten auch noch realistisch sein. Es nützt nichts,
SimpleXMLElement Object(
[SOAP:Body] => SimpleXMLElement Object
anzuzeigen, wenn man über ->SOAP:Body nicht an das Objekt rankommt.
Es scheint aber so, dass, wenn du den Namespace überwunden hast, der Rest auch in einem Rutsch angezeigt wird.
echo "$verabschiedung $name";
Hi dedlfix,
jetzt liefert mir $xml->children('http://www.w3.org/2003/05/soap-envelope')->children();
SimpleXMLElement Object ( ) SimpleXMLElement Object ( [site] => SimpleXMLElement Object ( [id] => 77 [english_name] => <SimpleXMLElement Object ( ) [name] => SimpleXMLElement Object ( ) [areas] => SimpleXMLElement Object ( [area] => SimpleXMLElement Object ( [id] => 20043 [english_name] => SimpleXMLElement Object ( ) [name] => SimpleXMLElement Object ( ) [language_code] => de [time_zone] => SimpleXMLElement Object ( ) [map_enabled] => true [currency_id] => 7 [currency_name] => SimpleXMLElement Object ( ) [sub_areas] => SimpleXMLElement Object ( [sub_area] => Array ( [0] => SimpleXMLElement Object ( [id] => 1103654 [english_name] => SimpleXMLElement Object ( ) [name] => SimpleXMLElement Object ( ) ) [1] => SimpleXMLElement Object ( [id] => 1103793 [english_name] => SimpleXMLElement Object ( ) [name] => SimpleXMLElement Object ( ) ) [2] => SimpleXMLElement Object ( [id] => 1103728 [english_name] => SimpleXMLElement Object ( ) [name] => SimpleXMLElement Object ( ) ) ) ) ) ) ) )
Das ist zwar schon einiges, aber noch nicht alles. Die Areas fehlen immer noch. Wo hast Du gemeint, dass Body bzw. Site oder Envelope aufgerufen werden soll ohne Namespace?
Müsste irgendwie bis zu -> site -> areas -> area ... gelangen. Aber durch Probieren komm ich da nicht hin.
VIELEN DANK und schönen Abend noch.
-Nina
echo $begrüßung;
jetzt liefert mir $xml->children('http://www.w3.org/2003/05/soap-envelope')->children();
SimpleXMLElement Object ( ) SimpleXMLElement Object ( [site] => SimpleXMLElement Object ( [id] => 77 [english_name] => <SimpleXMLElement Object ( ) [name] => SimpleXMLElement Object ( ) [areas] => SimpleXMLElement Object ( [area] => SimpleXMLElement Object ( [id] => 20043 [english_name] => [...]
Gib vor dem print_r() mal ein <pre> aus, oder schau dir die Ausgabe im HTML-Quelltext an. So sieht das doch grauslich aus.
Das ist zwar schon einiges, aber noch nicht alles. Die Areas fehlen immer noch.
Die sind doch da:
print_r($xml->children('http://www.w3.org/2003/05/soap-envelope')->children()->site->areas->area)
echo "$verabschiedung $name";
Die sind doch da:
print_r($xml->children('http://www.w3.org/2003/05/soap-envelope')->children()->site->areas->area)
In dieser Ausgabe erscheint nirgendwo Aachen oder ein anderes site->areas->area->name .
Habe nun nocheinmal explizit nach dieser Information bei php.net in der Doku gesucht. children gibts zwar, aber ich kann es nicht unbedingt auf meinen Fall übertragen.
Und auch die rekusiven Funktionen hierfür sind nicht gerade für die Kinder gemacht:
function RecurseXML($xml,&$vals,$parent="")
{
$child_count = 0;
foreach($xml as $key=>$value)
{
$child_count++;
$k = ($parent == "") ? (string)$key : $parent . "." . (string)$key;
if(RecurseXML($value,$vals,$k) == 0) // no childern, aka "leaf node"
$vals[$k] = (string)$value;
}
return $child_count;
}
Lese ich in der falschen Dokumentation oder woher hast Du das Wissen gewonnen? Dieses trial and error macht keinen Spass!
Schönen Abend noch.
echo $begrüßung;
Die sind doch da:
print_r($xml->children('http://www.w3.org/2003/05/soap-envelope')->children()->site->areas->area)
In dieser Ausgabe erscheint nirgendwo Aachen oder ein anderes site->areas->area->name .
Ich sag doch, SimpleXML verhält sich aufgrund der verwendeten PHP-Magie anders als Objekte ohne Magie. Diverse Dinge berechnet es erst bei einem direkten Zugriff. Manchmal sogar unterschiedlich.
$xml->children('http://www.w3.org/2003/05/soap-envelope')->children()->site->areas->area->name
Damit bekommst du ein Objekt. Wenn du an das Aachen willst, musst du das Objekt in einen String-Kontext bringen, damit intern __toString() aufgerufen wird und somit das Aachen aus dem CDATA befreit wird. Beispielsweise über
echo $xml->...
oder via Typecast
$name = (string) $xml->...
Dieses In-den-String-Kontext-Bringen passiert bei print_r() nicht, weswegen du da kein Aachen siehst.
Interessant wird es bei sub_area, denn davon gibt es mehrere. print_r(...->sub_areas) zeigt ein Array unterhalb von sub_area an, ein ->sub_areas->sub_area liefert dir aber nicht das Array sondern das erste Element daraus. Nichtsdestotrotz kann man mit foreach über ->sub_areas->sub_area iterieren und mittels Indices auf die einzelnen sub_area-Elemente zugreifen. An dieser Stelle spielen neben dem Overloading auch noch diverse Dinge der SPL mit.
Lese ich in der falschen Dokumentation
Weiß ich nicht, weil ich bisher nur durch Probleme anderer mit SimpleXML in Berührung kam, und mir das Thema weniger über die Dokumentation erarbeitet habe. Vielleicht gibt es anderswo im Netz ausführlichere Tutorials, die auch auf die Fälle eingehen, bei denen das "simple" in SimpleXML nicht mehr stimmt.
oder woher hast Du das Wissen gewonnen?
Ich weiß, dass es Overloading, "magische" Funktionen und die SPL gibt, und habe damit genügend Erfahrung gesammelt, so dass ich erkenne, dass von diesen Möglichkeiten bei SimpleXML reger Gebrauch gemacht wird. Teilweise leider nicht unbedingt nachvollziehbar für OOP-Anfänger bzw. -Normalnutzer.
echo "$verabschiedung $name";
Hi dedlfix,
immer mehr ärgert mich simplexml.
Es ist zwar richtig, dass mit
$xml->children('http://www.w3.org/2003/05/soap-envelope')->children()->site->areas->area->name
Aachen ausgegeben wird, aber was, wenn es nun mehr als nur eine <area> gibt?
Habe die Beispieldatei angepasst und scheitere nun erneut beim Versuch, den Namen der 2. area auszulesen.
Das wolltest Du mir vermutlich mit dem Beispiel der sub_areas verdeutlichen... Aber bevor man diese mit [index] verwenden kann, muss doch erst einmal deren Anzahl ausgelesen werden, oder?
Die php.net-Dokumentation zum Thema ist leider sehr bescheiden ausgefallen :(
Nachdem foreach einem das abnimmt, ist es nicht notwendig, extra die Länge des Arrays ->area bzw. ->sub_area auszulesen.
foreach($xml->children('http://www.w3.org/2003/05/soap-envelope')->Body->children()->site->areas->area AS $area)
{
print_r((string)$area->name);
}
Viel Spass damit.
echo $begrüßung;
Es ist zwar richtig, dass mit
$xml->children('http://www.w3.org/2003/05/soap-envelope')->children()->site->areas->area->name
Aachen ausgegeben wird, aber was, wenn es nun mehr als nur eine <area> gibt?
Das wolltest Du mir vermutlich mit dem Beispiel der sub_areas verdeutlichen... Aber bevor man diese mit [index] verwenden kann, muss doch erst einmal deren Anzahl ausgelesen werden, oder?
Die kannst du zum einen, wie auch bei echten Arrays üblich, mittels count(...->areas->area) ermitteln oder zum anderen einfach mit foreach nacheinander auf alle zugreifen, egal wieviele es sind.
Habe die Beispieldatei angepasst und scheitere nun erneut beim Versuch, den Namen der 2. area auszulesen.
->areas->area[1] (0 wäre die erste)
echo "$verabschiedung $name";