Analizzare l’XML significa essenzialmente la navigazione attraverso un documento in formato XML per la restituzione dei dati pertinenti. Un numero crescente di servizi web restituiscono i dati in formato JSON, ma un gran numero restituiscono ancora i dati in formato XML, quindi è necessario padroneggiare l’analisi dell’XML, se davvero si vuole servire dell’intera gamma di API disponibili.
Utilizzando l’estensione SimpleXML di PHP, che è stato introdotto nel PHP 5.0, lavorare con l’XML è molto facile. In questo post si mostra come fare.

Uso di base
Cominciamo con il seguente esempio con il file languages.xml:
[code lang=”html”] <?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>
[/code]
Il documento XML di sopra codifica una lista di linguaggi di programmazione e hanno due dettagli per ogni lingua: il suo anno di implementazione e il nome del suo creatore.

Il primo passo è quello di caricare il codice XML utilizzando la funzione simplexml_load_file() o simplexml_load_string(). Come ci si potrebbe aspettare, la prima carica il file XML da un file e la successiva carica il codice XML da una stringa data.

[code lang=”php”] <?php
$languages = simplexml_load_file("languages.xml");
?>
[/code]
Entrambe le funzioni leggono l’intero albero DOM in memoria e restituisce una rappresentazione dell’oggetto SimpleXMLElement. Nell’esempio precedente, l’oggetto viene memorizzato nella variabile $languages. È quindi possibile utilizzare var_dump() o print_r() per ottenere i dettagli dell’oggetto restituito, se volete.
[code lang=”xml”] 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
)
)
)
[/code]
Il codice XML contiene un elemento languages come radice che è avvolto tre elementi lang, motivo per cui il SimpleXMLElement ha lang come proprietà pubblica che sarebbe un array di tre SimpleXMLElements. Ciascun elemento dell’array corrisponde ad un elemento lang nel documento XML.

È possibile accedere alle proprietà dell’oggetto nel solito modo con l’operatore ->. Ad esempio, $languages->lang[0]] vi darà un oggetto SimpleXMLElement che corrisponde al primo elemento lang. Questo oggetto ha due proprietà pubbliche: appeared e creator.

[code lang=”php”] <?php
$languages->lang[0]->appeared;
$languages->lang[0]->creator;
?>
[/code]
Iterando nella lista dei languages per mostrare i loro dettagli può essere fatto molto facilmente con i metodi standard di looping, come foreach.
[code lang=”php”] <?php
foreach ($languages->lang as $lang) {
printf(
">%s apparso nel %d ed è stato creato da %s.",
$lang["name"],
$lang->appeared,
$lang->creator
);
}
?>
[/code]
Si noti l’accesso all’attributo name dell’elemento lang per recuperare il nome della lingua. È possibile accedere a qualsiasi attributo di un elemento rappresentato come un oggetto SimpleXMLElement utilizzando la notazione di array come questo di sopra.
Trattare con spazi dei nomi
Molte volte si incontrano elementi namespace durante l’utilizzo di XML da diversi servizi web. Andiamo a modificare il nostro esempio languages.xml per rispecchiare l’utilizzo degli spazi dei nomi
[code lang=”xml”] <?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>
[/code]
Adesso l’elemento creator è posto sotto il namespace dc che punta a http://purl.org/dc/elements/1.1/. Se si tenta di stampare il creatore di una linguaggio usando la tecnica precedente, non funzionerà. Per leggere gli elementi namespace come questo è necessario utilizzare uno dei seguenti approcci.

Il primo approccio è quello di utilizzare l’URI dello spazio dei nomi direttamente nel codice quando si accede a elementi namespace.
Nell’esempio riportato di seguito viene illustrato come:

[code lang=”php”] <?php
$dc = $languages->lang[1]- >children("http://purl.org/dc/elements/1.1/");
echo $dc->creator;
?>
[/code]
Il metodo children() prende uno spazio dei nomi e restituisce i figli dell’elemento preceduto. Accetta due argomenti: il primo è lo spazio dei nomi XML e il secondo è un valore booleano opzionale che per impostazione predefinita è su false. Se si passa true, lo spazio dei nomi sarà trattato come un prefisso piuttosto come l’URI del namespace attuale.

Il secondo approccio è quello di leggere l’URI dello spazio dei nomi dal documento e utilizzarlo durante l’accesso agli elementi del namespace. Questo è in realtà il modo più pulito per accedere agli elementi, perché non c’è bisogno di codificare l’URI.

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

echo $dc->creator;
?>
[/code]

Il metodo getNamespaces() restituisce un array con i prefissi dello spazio dei nomi con il loro URI associato. Accetta un parametro opzionale il cui valore predefinito è false. Se lo si imposta true allora il metodo restituirà gli spazi dei nomi utilizzati in nodi principali e secondari. In caso contrario, non trova spazi dei nomi utilizzati solo nel nodo genitore.

Ora è possibile scorrere l’elenco delle linguaggi in questo modo:

[code lang=”php”] <?php
$languages = simplexml_load_file("languages.xml");
$ns = $languages->getNamespaces(true);

foreach($languages->lang as $lang) {
$dc = $lang->children($ns["dc"]);
printf(
">%s apparso nel %d ed è stato creato da %s.",
$lang["name"],
$lang->appeared,
$dc->creator
);
}
?>
[/code]

Esaminiamo un esempio che recupera il feed RSS da un canale YouTube visualizzando i collegamenti a tutti i suoi video. Per questo abbiamo bisogno di fare una chiamata al seguente URL:
[code lang=”xml”] http://gdata.youtube.com/feeds/api/users//uploads
[/code]
L’URL restituisce un elenco degli ultimi video dal canale specificato in formato XML. Analizzando (parsando) il codice XML si ottenere i seguenti pezzi di informazioni per ogni video:

  • Video URL
  • Thumbnail
  • Title
Inizieremo con recuperare e caricare il codice XML:
[code lang=”php”] <?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);
?>
[/code]

Se si dà un’occhiata al feed XML si può vedere che ci sono diversi elementi entità (entity) ciascuno dei quali memorizza i dettagli di un video dal canale. Ma se ci interessa solo un’immagine in miniatura, URL del video e il titolo. I tre elementi sono figli del gruppo (group), che è un figlio dell’entrata (entry):
[code lang=”xml”] <entry> …
<media:group> …
<media:player url="video url"/>
<media:thumbnail url="video url" height="height" width="width"/>
<media:title type="plain">Title…</media:title> …
</media:group> …
</entry>
[/code]
Dobbiamo semplicemente scorrere tutti gli elementi entry, e per ogni loop è possibile estrarre le informazioni pertinenti. Si noti che nel player il thumbnail e il title sono tutti sotto lo spazio dei nomi dei media. Quindi, abbiamo bisogno di procedere come nell’esempio precedente: ottenere gli spazi dei nomi del documento e utilizzare lo spazio dei nomi durante l’accesso agli elementi.
[code lang=”ph”] <?php
foreach ($feed->entry as $entry) {
$group=$entry->children($ns["media"]);
$group=$group->group;
$thumbnail_attrs=$group->thumbnail[1]->attributes();
$image=$thumbnail_attrs["url"];
$player=$group->player->attributes();
$link=$player["url"];
$title=$group->title;
printf(‘<a href="%s"><img src="%s" alt="%s"></a>’,
$player, $image, $title);
}
?>
[/code]
Conclusione
Ora che sapete come usare SimpleXML per analizzare i dati XML, è possibile migliorare le nostre abilità analizzando diversi feed XML da diverse API. Ma un punto importante da considerare è che SimpleXML legge l’intero DOM in memoria, quindi se si sta analizzando grandi insiemi di dati allora si possono incontrare problemi di memoria. In questi casi si consiglia di usare qualcosa di diverso da SimpleXML, preferibilmente un evento basato di parser come XML Parser. Per ulteriori informazioni su SimpleXML, controllare la relativa documentazione.