This page was exported from Recursos para programadores [ https://eldesvandejose.com ]
Export date: Tue Oct 22 6:40:28 2019 / +0000 GMT

Leer XML con PHP (II)




En el artículo anterior aprendimos cómo podemos leer un XML para procesar sus datos en PHP. Resumiendo lo que vimos partimos de lo siguiente:



  • Ha quedado claro que necesitamos conocer la estructura del XML que nos llega, para poder procesarlo adecuadamente.

  • El proceso básico es siempre el mismo: leer los elementos y atributos de los nodos.

  • El mismo proceso se repite, dependiendo de la estructura de anidamiento de nodos y sub-nodos. Cuanto más complejo sea el XML, más lecturas deberemos hacer para recuperar todos sus datos.



En la mayoría de los casos, cuando nos llega un XML para procesar con PHP es parte de un proceso para intercambio de datos entre dos plataformas web diferentes (lo que, por supuesto, es la razón de ser de los formatos de intercambio de datos, como es XML). Eso quiere decir que el proceso que haya creado el XML habrá generado, en la mayoría de los casos, un XML, digámoslo así: simple, es decir, sin nada más que el propio XML con su estructura de nodos, elementos y atributos.


NAMESPACES


A pesar de o comentado, en aplicaciones que lean fuentes similares de varios orígenes es posible que el archivo XML incluya el uso de espacios de nombres. Una cosa que vamos a aprender es a leer los namespaces de un XML, si los tiene. Para ello vamos a partir del XML que hemos empleado en esta serie de artículos, levemente modificado. Lo hemos llamado obras_NS.xml:


<?xml version="1.0" encoding="UTF-8"?>
<ns_obras_origen_1:obras xmlns:ns_obras_origen_1='http://origen_1.com/ns/obras' xmlns:ns_personal_origen_1='http://origen_1.com/ns/personal'>
<ns_obras_origen_1:obra inicio="10/02/2015" final="31/10/2016" contratista="Actuaciones Urbanas" presupuesto="20.000.000">
Construccion de aparcamiento en el centro
<ns_personal_origen_1:personal_tecnico miembros="3">
<ns_personal_origen_1:miembro cargo="arquitecto">Pedro Steven De Gloria</ns_personal_origen_1:miembro>
<ns_personal_origen_1:miembro cargo="aparejador">Manuela Gracia Salmerón</ns_personal_origen_1:miembro>
<ns_personal_origen_1:miembro cargo="supervisor">Andrés Garrido Fuentes</ns_personal_origen_1:miembro>
</ns_personal_origen_1:personal_tecnico>
</ns_obras_origen_1:obra>
<ns_obras_origen_1:obra inicio="06/08/2016" final="01/11/2016" contratista="Obras del Norte" presupuesto="6.500.000">
Adaptación de estación suburbana
<ns_personal_origen_1:personal_tecnico miembros="4">
<ns_personal_origen_1:miembro cargo="arquitecto">Manuel Alarcón Rodríguez</ns_personal_origen_1:miembro>
<ns_personal_origen_1:miembro cargo="aparejador">Carlos Torres Fuentes</ns_personal_origen_1:miembro>
<ns_personal_origen_1:miembro cargo="director_de_tunelacion">María García Pérez</ns_personal_origen_1:miembro>
<ns_personal_origen_1:miembro cargo="jefe_de_compras">Antonia Bisonette Tristán</ns_personal_origen_1:miembro>
</ns_personal_origen_1:personal_tecnico>
</ns_obras_origen_1:obra>
<ns_obras_origen_1:obra inicio="02/02/2014" final="26/05/2017" contratista="Iluminación y Electricidad, SA" presupuesto="7.800.000">
Electrificación de zona restringida
<ns_personal_origen_1:personal_tecnico miembros="2">
<ns_personal_origen_1:miembro cargo="jefe_de_electricistas">Laura De la Iglesia Cifuentes</ns_personal_origen_1:miembro>
<ns_personal_origen_1:miembro cargo="responsable_de_control">Yolanda Torres Torres</ns_personal_origen_1:miembro>
</ns_personal_origen_1:personal_tecnico>
</ns_obras_origen_1:obra>
</ns_obras_origen_1:obras>







ATENCIÓN. Las URI que figuran como xmlns sólo incluyen, llegado el caso, información legible para lectores humanos. En ningun caso son procesadas por el analizador XML.

La matriz de resultados que queremos obtener es la siguiente (seguro que ya te suena familiar):


array(3) {
[0]=>
array(7) {
["obra"]=>string(41) "Construccion de aparcamiento en el centro"
["fecha_de_inicio"]=>string(10) "10/02/2015"
["fecha_de_finalizacion"]=>string(10) "31/10/2016"
["contratista"]=>string(19) "Actuaciones Urbanas"
["presupuesto"]=>string(10) "20.000.000"
["miembros_tecnicos"]=>string(1) "3"
["personal_tecnico"]=>
array(3) {
["arquitecto"]=>string(22) "Pedro Steven De Gloria"
["aparejador"]=>string(24) "Manuela Gracia Salmerón"
["supervisor"]=>string(23) "Andrés Garrido Fuentes"
}
}
[1]=>
array(7) {
["obra"]=>string(34) "Adaptación de estación suburbana"
["fecha_de_inicio"]=>string(10) "06/08/2016"
["fecha_de_finalizacion"]=>string(10) "01/11/2016"
["contratista"]=>string(15) "Obras del Norte"
["miembros_tecnicos"]=>string(1) "4"
["presupuesto"]=>string(9) "6.500.000"
["personal_tecnico"]=>
array(4) {
["arquitecto"]=>string(26) "Manuel Alarcón Rodríguez"
["aparejador"]=>string(21) "Carlos Torres Fuentes"
["director_de_tunelacion"]=>string(21) "María García Pérez"
["jefe_de_compras"]=>string(26) "Antonia Bisonette Tristán"
}
}
[2]=>
array(7) {
["obra"]=>string(36) "Electrificación de zona restringida"
["fecha_de_inicio"]=>string(10) "02/02/2014"
["fecha_de_finalizacion"]=>string(10) "26/05/2017"
["contratista"]=>string(31) "Iluminación y Electricidad, SA"
["presupuesto"]=>string(9) "7.800.000"
["miembros_tecnicos"]=>string(1) "2"
["personal_tecnico"]=>
array(2) {
["jefe_de_electricistas"]=>string(29) "Laura De la Iglesia Cifuentes"
["responsable_de_control"]=>string(21) "Yolanda Torres Torres"
}
}
}

Vamos  a comentar un par de detalles antes de listar el código que nos permite leer este archivo.


Observa, en el XML, que el nodo obras (que es el nodo raíz) y cada uno de los nodos obra tienen el mismo espacio de nombres (ns_obras_origen_1). A los efectos, cómo el nodo raíz sólo tiene los sob-nodos obra (no tiene atributos ni elemento) solo nos interesa el espacio de nombres desde el punto de vista de cada nodo obra.


Con el personal técnico la cosa es diferente. Tanto el nodo personal_tecnico como sus sub-nodos miembro comparten el espacio de nombres ns_personal_origen_1. Dado que, cuando utilizamos espacios de nombres los nodos son identificados a través de estos, tendremos que tenerlo en cuenta al leer los datos de cada miembro del personal técnico, así como su correspondiente atributo. En seguida lo veremos. El código, que hemos llamado leer_NS.php, es el siguiente:


<?php
echo "<pre>";
/* Creamos una matriz general para almacenar los datos
que recuperemos del XML en un formato procesabledesde PHP
(para una base de datos, una renderización en pantalla o
lo que sea necesario). */
$matrizDeObras = array();

/* Creamos un objeto de la clase SimpleXMLElement para
almacenar en el el contenido del archivo XML en una
estructura de objeto procesable. */
$contenidoXML = new SimpleXMLElement("obrasNS.xml", 0, true);

/* Determinamos los espacios de nombres empleados. */
$espaciosDeNombres = $contenidoXML->getNamespaces(true);

$nodos_NS_Obra = $contenidoXML->children($espaciosDeNombres['ns_obras_origen_1']);

/* con el nombre de los nodos creamos unas variables
(una por nodo) que podemos reorrer para trabajar sobre ellas. */
foreach ($nodos_NS_Obra as $obra) {
$elementoObra = array(); // Elemento que contendrá datos de cada nodo.
$elementoObra["obra"] = trim((string)$obra); // Se agrega el contenido del nodo como cadena.

/* Obtenemos los atributos del nodo con el método attributes().*/
$atributosDeNodoObra = $obra->attributes();
/* Recorremos todos los atributos asignándolos a la matriz
del nodo actual. */
$elementoObra["fecha_de_inicio"] = (string)$atributosDeNodoObra->inicio;
$elementoObra["fecha_de_finalizacion"] = (string)$atributosDeNodoObra->final;
$elementoObra["contratista"] = (string)$atributosDeNodoObra->contratista;
$elementoObra["presupuesto"] = (string)$atributosDeNodoObra->presupuesto;

/* Recuperamos los nodos que están dentro del
NS 'ns_personal_origen_1', dentro de cada nodo 'obra' */
$nodos_NS_personal = $obra->children($espaciosDeNombres['ns_personal_origen_1']);
/*Obtenemos el atributo 'miembros' del nodo primario del
NS 'ns_personal_origen_1', lo que nos da el número de miembros. */
$elementoObra["miembros_tecnicos"] = (string)$nodos_NS_personal->attributes()->miembros;

/* Creamos una submatriz auxiliar para guardar los cargos y nombres
de los miembros técnicos.
Debemos tener en cuenta que el nodo 'personal_tecnico' y los
sub-nodos 'miembro' comparten el mismo espacio de nombres, por
lo que debemos referirnos al primer elemmento de dicho NS
(el que tiene el índice [0]) y, a partir de ahí,
recorrer cada uno de los miembros que encontremos dentro.*/
$elementoObra["personal_tecnico"] = array();
foreach ($nodos_NS_personal[0] as $directivo){
$elementoObra["personal_tecnico"][(string)$directivo->attributes()["cargo"]] = trim((string)$directivo);
}

$matrizDeObras[] = $elementoObra; // El elemento de cada nod es agregado a la matriz general.
unset($elementoObra); // El contenido de un elemento nodo se recreará en cada iteración.
}
var_dump ($matrizDeObras);
?>

Aunque el código es muy similar al del artículo anterior, el uso de namespaces nos obliga a retocar algunos detalles. En primer lugar, debemos obtener una matriz con los espacios de nombres que se han encontrado en el XML, así:


$espaciosDeNombres = $contenidoXML->getNamespaces(true);


El parámetro true hace que se busquen, recursivamente, todos los espacios de nombres, de modo que no se "salte" ninguno por estar anidado dentro de otro.


Los nodos obra son identificados a través de su espacio de nombres, así:


$nodos_NS_Obra = $contenidoXML->children($espaciosDeNombres['ns_obras_origen_1']);


Por lo tanto, para recorrer cada uno, cambiamos un poco el bucle, así:


foreach ($nodos_NS_Obra as $obra) {


De hacerlo cómo en el ejercicio del artículo anterior, no se encontrarían los nodos obra, porque el espacio de nombres los encapsula, ocultándolos a la lectura "normal".


Dentro de cada nodo obra, los nodos del personal técnico también deben ser identificados por su espacio de nombres, así:


$nodos_NS_personal = $obra->children($espaciosDeNombres['ns_personal_origen_1']);


Cómo el espacio de nombres aquí encapsula al nodo personal_tecnico y a sus sub-nodos miembro, para acceder a cada miembro individual debemos recorrer el nodo personal_tecnico, que es el primero del espacio de nombres, por lo que debemos emplear su índice (que es el [0]), así:


foreach ($nodos_NS_personal[0] as $directivo){


EN CONCLUSIÓN


Cómo ves, el uso de espacios de nombres permite una mayor encapsulación de los datos, a costa de un poco más de complejidad en el código pero, si a tu aplicación le llega un XML con espacios de nombres, deberás tenerlo en cuenta.

Post date: 2016-09-03 16:08:40
Post date GMT: 2016-09-03 16:08:40
Post modified date: 2016-10-06 09:46:41
Post modified date GMT: 2016-10-06 09:46:41
Powered by [ Universal Post Manager ] plugin. HTML saving format developed by gVectors Team www.gVectors.com