Parsowanie XML z SimpleXML w PHP

XML SimpleXML PHP

Parsowanie składni XML oznacza zwykle nawigowanie po całej strukturze dokumentu XML. Duża liczba dzisiejszych serwisów zwraca dane w formacie JSON, ale jest jeszcze sporo takich, które używają do tego celu XML.

W dzisiejszym HowTo wykorzystam rozszerzenie SimpleXML, które wprowadzone zostało do PHP 5.0. Pozwala ono na łatwe nawigowanie po dokumencie XML.

Przejdźmy do pierwszego przykładu wykorzystania. Mamy do dyspozycji plik languages.xml:

<?xml version="1.0" encoding="utf-8"?>
<languages>
 <lang name="C">
  <appeared>1972</appeared>
  <creator>Dennis Ritchie</creator>
 </lang>
 <lang name="PHP">
  <appeared>1995</appeared>
  <creator>Rasmus Lerdorf</creator>
 </lang>
 <lang name="Java">
  <appeared>1995</appeared>
  <creator>James Gosling</creator>
 </lang>
</languages>

Powyższy dokument opisuje w pewien sposób trzy języki programowania i podaje ich kilka charakterystyk.

Do odczytania tego dokumentu wykorzystam funkcję simple_load_file().

<?php
$languages = simplexml_load_file("languages.xml");

Istnieje jeszcze funkcja simplexml_load_string(), która jak nie trudno się domyślić, ładuje do pamięci łańcuch znaków, który jest poprawną składnią dokumentu XML. Obie funkcje wczytują dokument DOM do pamięci tworząc tym samym obiekt SimpleXMLElement.

Wykonując var_dump($languages) lub print_r($languages) otrzymamy:

SimpleXMLElement Object
(
    [lang] => Array
        (
            [0] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [name] => C
                        )
                    [appeared] => 1972
                    [creator] => Dennis Ritchie
                )
            [1] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [name] => PHP
                        )
                    [appeared] => 1995
                    [creator] => Rasmus Lerdorf
                )
            [2] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [name] => Java
                        )
                    [appeared] => 1995
                    [creator] => James Gosling
                )
        )
)

Widać tutaj główny, publiczny element lang (SimpleXMLElement), który jest tablicą i zawiera trzy elementy.

By dostać się do konkretnych elementów trzeba wykorzystać operator ->

<?php
$languages->lang[0]->appeared;
$languages->lang[0]->creator;

Można także iterować po liście za pomocą metody foreach

<?php
foreach ($languages->ang as $lang) {
    echo "Language: ".$lang["name"];
    echo "Appeared: ".$lang->appeared;
    echo "Creator: ".$lang->creator;
}

Należy zwrócić uwagę, że do wartości atrybutu lang docieramy za pomocą parametru w tablicy, a do publicznych elementów appeared i creator za pomocą operatora ->.

Wszystko fajnie, pięknie, ale co jeżeli trzeba sobie poradzić z przestrzenią nazw, która zdefiniowana została dla poszczególnych elementów? Jest i na to sposób!

Zmodyfikuję teraz plik languages.xml do postaci:

<?xml version="1.0" encoding="utf-8"?>
<languages
 xmlns:dc="http://purl.org/dc/elements/1.1/">
 <lang name="C">
  <appeared>1972</appeared>
  <dc:creator>Dennis Ritchie</dc:creator>
 </lang>
 <lang name="PHP">
  <appeared>1995</appeared>
  <dc:creator>Rasmus Lerdorf</dc:creator>
 </lang>
 <lang name="Java">
  <appeared>1995</appeared>
  <dc:creator>James Gosling</dc:creator>
 </lang>
</languages>

Element creator posiada teraz namespace, który kieruje na http://purl.org/dc/elements/1.1/. Dostęp do tego elementu poprzez wykorzystanie operatora -> nie jest już możliwy.

By poradzić sobie z tym problemem trzeba zrobić coś takiego:

<?php
$dc = $languages->lang[1]- >children("http://purl.org/dc/elements/1.1/");
echo $dc->creator;

Metoda children() pobiera namespace i zwraca wartość elementu dla którego dc jest prefixem.

Istnieje inna możliwość odczytania wartości elementu creator:

<?php
$namespaces = $languages->getNamespaces(true);
$dc = $languages->lang[1]->children($namespaces["dc"]);

echo $dc->creator;

Pobieramy w tym celu wszystkie namespace użyte w dokumencie XML i za pomocą elementu w tablicy odwołujemy się do konkretnego namespace i pobieramy wartość dla którego jest on prefixem.

I podobnie jak wcześniej – przykład iterowania po wszystkich elementach:

<?php
$languages = simplexml_load_file("languages.xml");
$ns = $languages->getNamespaces(true);

foreach ($languages->lang as $lang) {
    $dc = $lang->children($ns["dc"]);
    echo "Language: ".$lang["name"];
    echo "Appeared: ".$lang->appeared;
    echo "Creator: ".$dc->creator;
}

Czas na praktyczny przykład. Parsowanie kanału YouTube.

W tym celu należy wykorzystać API dostępne pod adresem: http://gdata.youtube.com/feeds/api/users/{channel}/uploads, gdzie w miejscu {channel} podana zostanie nazwa konkretnego użytkownika.

W danych, które zostaną zwrócone w przypadku podania poprawnej nazwy użytkownika, dla każdego filmu zdefiniowane będą:

  • jego URL
  • miniatura
  • tytuł
<?php
$channel = "channelName";
$url = "http://gdata.youtube.com/feeds/api/users/".$channel."/uploads";
$xml = file_get_contents($url);

$feed = simplexml_load_string($xml);
$ns = $feed->getNameSpaces(true);

Spoglądając w strukturę otrzymanego XML możemy zauważyć, że ważne dla nas dane znajdują się w elemencie group, który poprzedzony jest namespace media.

<entry>
   …
   <media:group>
      …
      <media:player url="video url"/>
      <media:thumbnail url="image url" height="height" width="width"/>
      <media:title type="plain">Title…</media:title>
      …
   </media:group>
   …
</entry>

Wystarczy, że ponownie wykorzystana zostanie metoda foreach, dzięki której będziemy w stanie wyświetlić odpowiednie dla nas dane.

<?php
foreach ($feed->entry as $entry) {
	$group = $entry->children($ns["media"]);
	$group = $group->group;
	$thumbnailAttrs = $group->thumbnail[1]->attributes();
	$image = $thumbnailAttrs["url"];
	$player = $group->player->attributes();
	$link = $player["url"];
	$title = $group->title;
	
	echo "<p><a href=".$link."><img src=".$image." alt=".$title."></a></p>";
}

Podsumowując. Operowanie na dokumentach XML jest bardzo proste. Dzięki wykorzystaniu rozszerzenia SimpleXML, drzewo DOM nie jest już dla nas problemem.

Pełną dokumentację SimpleXML znajdziecie pod adresem: http://php.net/manual/en/book.simplexml.php