Leer archivos XML con varios namespaces con SimpleXML

Cada vez es más habitual trabajar con ficheros XML para todo tipo de tareas, como archivos de configuración, o intercambio de datos entre aplicaciones. En PHP5 disponemos de SimpleXML, una excelente herramienta para manejar este tipo de archivos, sobretodo comparado con lo arduo que era en versiones anteriores de PHP.

Lo más habitual cuando trabajamos con archivos XML, sobretodo si los utilizamos como archivos de configuración, es que todas las etiquetas pertenezcan al mismo namespace, pero esto no es siempre así. En este post voy a explicar como utilizar SimpleXML para procesar archivos XML que contienen etiquetas que hacen referencia a diferentes namespaces, como por ejemplo un archivo RDF o RSS.

Voy a utilizar como ejemplo el feed de RDF que utiliza del.icio.us para mostrar los bookmarks más recientes realizados por los usuarios.

El objetivo es mostrar un listado con las páginas añadidas por los usuarios, con la información del usuario, la fecha, y las etiquetas utilizadas.

El siguiente fragmento muestra uno de estos elementos tal como viene en el feed de del.icio.us, con la información de un bookmark realizado por el usuario paulantoine.

 
<?xml version="1.0" encoding="UTF-8" ?> 
<rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/" xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/">
<channel rdf:about="http://feeds.delicious.com">
<title>Delicious recent</title> 
<link>http://delicious.com/recent</link> 
<description>the most recent bookmarks</description> 
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://www.devshop.com/" /> 
</rdf:Seq>
</items>
</channel>
<item rdf:about="http://www.devshop.com/">
<title>Devshop.com - Really smart project management, just for software teams.</title> 
<dc:date>2009-01-07T10:39:51Z</dc:date> 
<link>http://www.devshop.com/</link> 
<dc:creator>paulantoine</dc:creator> 
<dc:subject>project_management development</dc:subject> 
<taxo:topics>
<rdf:Bag>
<rdf:li rdf:resource="http://delicious.com/tag/project_management" /> 
<rdf:li rdf:resource="http://delicious.com/tag/development" /> 
</rdf:Bag>
</taxo:topics>
</item>
</rdf:RDF>

La etiqueta raíz (rdf:RDF) contiene el conjunto de namespaces que se utilizan en el XML, empezando por el de defecto (xmlns) y siguiendo con xmlns:rdf, xmlns:content, etc. Dentro del XML las diferentes etiquetas y atributos hacen referencia a algunos de estos namespaces.

Para obtener el listado de los bookmarks con su información es necesario recorrer los nodos item utilizando SimpleXML. El siguiente fragmento de código lista todas las páginas añadidas por los usuarios.

$xml = new SimpleXMLElement('http://del.icio.us/rss/recent?min=1');
foreach ($xml->item as $item){
   echo "Página: ".$item->link."\n";
}

Para mostrar la información del usuario que añade la página tenemos que mostrar la información que hay en el nodo dc:creador. En este caso el nodo contiene un namespace, por lo que si utilizamos $item->creador para intentar obtenerlo, no obtendremos nada, ya que ese nodo en realidad no existe.

Para poder acceder a esa información debemos obtener primero todos los nodos hijos del elemento $item que pertenezcan al namespace dc, pasándole el namespace como parámetro al método children. Una vez hecho esto, el elemento obtenido contendrá los nodos con la información del usuario y la fecha.

$dc = $item->children(‘http://purl.org/dc/elements/1.1/’);
echo "\tUsuario: ".$dc->creator."\n";
echo "\tFecha: ".$dc->date."\n";

A continuación se muestra el código completo que permite mostrar toda la información de páginas del RSS. Al comienzo de él creo una matriz con todos los namespaces que voy a utilizar en el código, con el fin de no repetir lar url.

$ns = array(
   'rss' => "http://purl.org/rss/1.0/", 
   'rdf' => "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 
   'content' => "http://purl.org/rss/1.0/modules/content/", 
   'taxo' => "http://purl.org/rss/1.0/modules/taxonomy/",
   'dc' => "http://purl.org/dc/elements/1.1/"
);
 
echo "--------------------------------------------------\n";
 
// Cargo el archivo XML con la información
$xml = new SimpleXMLElement('http://del.icio.us/rss/recent?min=1');
 
// recorro los item del XML
foreach ($xml->item as $item){
 
   echo "Página: ".$item->link."\n";
 
   // accedo al namespace dc
   $dc = $item->children($ns['dc']);
 
   echo "\tUsuario: ".$dc->creator."\n";
   echo "\tFecha  : ".$dc->date."\n";
 
   // accedo al namespace taxo
   $taxo = $item->children($ns['taxo']);
 
   // si se ha asignado algún tag a la página
   if(isset($taxo->topics)){
 
      // obtengo el conjunto de tags a través
      // del namespace rdf 
      $bag = $taxo->children($ns['rdf'])->Bag;
 
      foreach($bag->li as $li){
 
         // obtengo el tag utilizando el namespace para acceder al atributo
         $tag = $li->attributes($ns['rdf'])->resource;
         $tag = str_replace('http://delicious.com/tag/','',$tag);
 
         echo "\tTag    : ".$tag."\n";
      }
   }
}
Twitter Digg Delicious Stumbleupon Technorati Facebook Email

2 Respuestas para “Leer archivos XML con varios namespaces con SimpleXML”

  1. Si tengo un archivo en xml que tiene namespaces ya diseñados, le puedo colocar una hoja de estilo css a sus elemntos como haria

    uni.2009.torres_sr
    torres_sr

    Por ejemplo como puedo poner uni.2009.torres_sr por ejemplo de color rojo??

  2. Muy bueno este artículo, me acaba de salvar de bucle mental je je.