with simplexmlelement read namespace leer con php xml namespaces nested simplexml

read - simplexmlelement php xpath



PHP simpleXML cómo verificar si existe un niño anidado (4)

Parece que debería usar XPath según este enlace .

Tengo que procesar unos 750 archivos xml para generar una buena relación. Probablemente debería haber usado XSLT o usar XPath, pero probablemente sea demasiado tarde para eso. Entonces mi pregunta; para los primeros dos registros todo funciona bien. Parece que hay un par de archivos XML sin los nodos a los que llamo. He intentado usar isset y !== null , que no funciona y me da el mismo error. A saber

Aviso: intentar obtener la propiedad de no objeto en /var/www/overzicht/script.php en la línea 38
Aviso: intentar obtener la propiedad de no objeto en /var/www/overzicht/script.php en la línea 38
Error fatal: Llamar a una función miembro children () en un no objeto en /var/www/overzicht/script.php en la línea 38

Usar lo siguiente probablemente sea incorrecto, ¿verdad?

if($xml_entry->children(''http://www.isotc211.org/2005/gmd'')->identificationInfo->MD_DataIdentification->citation->CI_Citation->title->children(''http://www.isotc211.org/2005/gco''))

Una pequeña muestra del archivo XML que estoy tratando de analizar es (todo el xml se puede encontrar aquí :

<gmd:contact> <gmd:CI_ResponsibleParty> <gmd:individualName> <gco:CharacterString>B. Boers</gco:CharacterString> </gmd:individualName> <gmd:organisationName> <gco:CharacterString>Staatsbosbeheer</gco:CharacterString> </gmd:organisationName> <gmd:positionName> <gco:CharacterString>Contactpersoon</gco:CharacterString> </gmd:positionName> </gmd:CI_ResponsibleParty> </gmd:contact>

Y mi PHP:

<?php $xml_url = "http://www.nationaalgeoregister.nl/geonetwork/srv/dut/q?fast=index&from=1&to=10000&geometry=POLYGON((5.5963%2053.3162%2C5.5963%2053.5766%2C6.9612%2053.5766%2C6.9612%2053.3162%2C5.5963%2053.3162))"; $xml_single_url = "http://www.nationaalgeoregister.nl/geonetwork/srv/dut/xml.metadata.get?uuid="; //Load the XML $xml = simplexml_load_file($xml_url); $xml_array = array(); //Loop through all the nodes with ''metadata'' and put uuid in the array foreach($xml->metadata as $metadata) { $xml_array[] = $metadata->children(''http://www.fao.org/geonetwork'')->children()->uuid; } echo "<table>" ."<tr>" ."<td>Title</td>" ."<td>Owner</td>" ."<td>Purpose</td>" ."<td>Tags</td>" ."<td>Url</td>" ."<td>Url</td>" ."</tr>"; $i = 0; //For every id in the $xml_array foreach($xml_array as $ar) { //Just a limit for testing purposes $i++; if($i == 100) { break; } //Loads the xml file $xml_entry = simplexml_load_file($xml_single_url .$ar); echo "<tr>"; //Title echo "<td>" .$xml_entry->children(''http://www.isotc211.org/2005/gmd'')->identificationInfo->MD_DataIdentification->citation->CI_Citation->title->children(''http://www.isotc211.org/2005/gco'')->CharacterString ."</td>"; //Owner echo "<td>" .$xml_entry->children(''http://www.isotc211.org/2005/gmd'')->contact->CI_ResponsibleParty->organisationName->children(''http://www.isotc211.org/2005/gco'')->CharacterString ."</td>"; //Purpose echo "<td>" .$xml_entry->children(''http://www.isotc211.org/2005/gmd'')->identificationInfo->MD_DataIdentification->purpose->children(''http://www.isotc211.org/2005/gco'')->CharacterString ."</td>"; //Tags //Transfer echo "</tr>"; } echo "</table>"; ?>

Intenté encontrar la solución por mi cuenta, pero parece que no puedo encontrarla.


El problema que tienes es que tienes una larga cadena de operadores -> , y el elemento que falta está en algún lugar de esa cadena . Tan pronto como solicite un elemento que no existe, obtendrá un NULL, y todos los operadores subsiguientes fallarán en algún grado u otro.

Teóricamente, si no tiene idea de cuál de los elementos de la cadena falta (y tal vez lo haga basándose en la estructura conocida / permitida del XML?) Tendría que dividir la cadena en una serie completa de asignaciones intermedias y isset() cheques.

Afortunadamente, PHP le permite salirse con llamadas como null->Property con solo un Notice , por lo que solo es la llamada al método ->children() lo que causará un error fatal. Entonces, podría verificar antes de cada llamada a eso:

if( ! isset($xml_entry) { return; } $temp = $xml_entry->children(''http://www.isotc211.org/2005/gmd'')->identificationInfo->MD_DataIdentification->citation->CI_Citation->title; if( ! isset($temp) { return; } echo $temp->children(''http://www.isotc211.org/2005/gco''))->CharacterString;

Sin embargo, el mensaje de error le dice más de lo que puede haberse dado cuenta:

  1. Aviso: intentar obtener la propiedad de no objeto en /var/www/overzicht/script.php en la línea 38
  2. Aviso: intentar obtener la propiedad de no objeto en /var/www/overzicht/script.php en la línea 38
  3. Error fatal: Llamar a una función miembro children () en un no objeto en /var/www/overzicht/script.php en la línea 38

Son dos Notice sobre el acceso a las propiedades y un Fatal error sobre el acceso a un método. Entonces la línea debe romperse así ...

$xml_entry ->children(''http://www.isotc211.org/2005/gmd'') ->identificationInfo ->MD_DataIdentification // OK to here ->citation // This part didn''t complain, but subsequent ones did; <citation> is the missing element ->CI_Citation // First Notice ->title // Second Notice ->children(''http://www.isotc211.org/2005/gco'')) // Fatal error - processing aborts here ->CharacterString

Entonces, lo que necesita verificar es la existencia de una <citation> :

$citation = $xml_entry->children(''http://www.isotc211.org/2005/gmd'')->identificationInfo->MD_DataIdentification->citation; if ( isset($citation) ) { echo $citation->CI_Citation->title->children(''http://www.isotc211.org/2005/gco'')->CharacterString; }


Su código de análisis funciona bien con su muestra XML. Puede ver en codepad.viper-7.com/6oLCEZ y en 3v4l.org/pW7Wu .

Si es la primera llamada a children() que se queja, entonces parece que simplexml_load_file ha fallado. Devuelve FALSE en caso de error, por lo que debe verificarlo.

if (FALSE === $xml_entry) { echo ''could not load file''; }

Más información en documentos aquí . Quizás la URL sea incorrecta, no válida o no devuelva XML válido.

De lo contrario, parece que faltan elementos en el XML real que causa el error. Puede verificar los elementos que faltan utilizando property_exists() como este ...

$gmd = $xml_entry->children(''http://www.isotc211.org/2005/gmd''); if (property_exists($gmd, ''identificationInfo'')) { $id_info = $gmd->identificationInfo; } if (isset($id_info) && property_exists($id_info, ''MD_DataIdentification'')) { $md_data_id = $id_info->MD_DataIdentification; } if (isset($md_data_id) && property_exists($md_data_id, ''citation'')) { $citation = $md_data_id->citation; } if (isset($citation) && property_exists($citation, ''CI_Citation'')) { $ci_citation = $citation->CI_Citation; } if (isset($ci_citation) && property_exists($ci_citation, ''title'')) { $title = $ci_citation->title; } if (isset($title)) { $gco = $title->children(''http://www.isotc211.org/2005/gco''); } //Title echo "<td>"; if (isset($gco) && property_exists($gco, ''CharacterString'')) { echo $gco->CharacterString; } echo "</td>";

Véalo en 3v4l.org/0DTjI . Y eso sin mencionar la posibilidad de tener múltiples elementos con el mismo nombre. Entonces, teniendo en cuenta todo eso, puede que no sea demasiado tarde para ir por la ruta XPath después de todo ;-)

$title = $xml_entry->xpath(''/gmd:MD_Metadata/gmd:identificationInfo/gmd:MD_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:title/gco:CharacterString''); echo "<td>"; if (isset($title[0])) { $title[0]; } echo "</td>";


el problema con líneas como estas:

if($xml_entry->children(''http://www.isotc211.org/2005/gmd'')->identificationInfo->MD_DataIdentification->citation->CI_Citation->title->children(''http://www.isotc211.org/2005/gco''))

es que son demasiado largos y propensos a errores. Incluso SimpleXML permite ese tipo de acceso "fácil" aquí, en caso de que no encuentre el elemento en alguna parte, devolverá NULL y luego obtendrá las advertencias e incluso los errores fatales.

Para su caso de uso, es mucho mejor usar una consulta xpath para hacer el trabajo. Como necesita acceder a múltiples propiedades que representan los metadatos, le sugiero que primero incluya esto en una clase de su propio ejemplar SimpleXMLElementXpathObject , el PropertyIterator usado allí se puede encontrar aquí .

Este tipo le permite definir los metadatos que busca con un SimpleXMLElement y una matriz que describe las propiedades asignándolas a las consultas de xpath:

$metaDef = array( ''title'' => ''gmd:identificationInfo//gmd:CI_Citation/gmd:title/gco:CharacterString'', ''owner'' => ''gmd:contact/gmd:CI_ResponsibleParty/gmd:organisationName/gco:CharacterString'', ''purpose'' => ''gmd:identificationInfo/gmd:MD_DataIdentification/gmd:purpose/gco:CharacterString'', );

Como puede ver, hay una expresión xpath por cada clave. Las claves se convertirán en propiedades. Esto luego le permite hacer las asignaciones sobre la marcha, por ejemplo:

$meta = new SimpleXMLElementXpathObject($xml, $metaDef); echo $meta->title, "/n"; echo json_encode($meta, JSON_PRETTY_PRINT), "/n";

Salida:

Natuur - Ecologische verbindingszones { "title": "Natuur - Ecologische verbindingszones", "owner": "provincie Frysl/u00e2n", "purpose": "Beleidsnota /"ecologische verbindingszones in Frysl/u00e2n/" vastgesteld door Provinciale Staten op 4 oktober 2006. Opgenomen in het Streekplan 2007" }

En caso de que xpath no devuelva ningún resultado, se proporciona NULL. Eso significa que las propiedades son opcionales, no verá advertencias o incluso errores fatales. Solo para dejarlo en claro: Esto es básicamente usando el método xpath de SimpleXMLElement para que también pueda ejecutar estas consultas por su cuenta.

Un ejemplo más completo:

$query = new GeoNetwork_Query(); $query ->setGeometry(''POLYGON((5.5963 53.3162,5.5963 53.5766,6.9612 53.5766,6.9612 53.3162,5.5963 53.3162))'') ->setLimit(10); $metaObj = function (GeoNetwork_Resource $resource) { $metaDef = array( ''title'' => ''gmd:identificationInfo//gmd:CI_Citation/gmd:title/gco:CharacterString'', ''owner'' => ''gmd:contact/gmd:CI_ResponsibleParty/gmd:organisationName/gco:CharacterString'', ''purpose'' => ''gmd:identificationInfo/gmd:MD_DataIdentification/gmd:purpose/gco:CharacterString'', ); return new SimpleXMLElementXpathObject($resource->getIterator(), $metaDef); }; $resources = new GeoNetwork_UuidIterator($query); $objects = new DecoratingIterator($resources, $metaObj); $table = new HtmlTableIterator($objects, [''Title'', ''Owner'', ''Purpose'']); echo "<table>/n"; foreach ($table as $row) { echo $row, "/n"; } echo "</table>/n";

He limitado el resultado a 10 para que no cree una lista demasiado larga (para el resultado de la consulta). También puede limitar los $objects envolviéndolos en un LimitIterator . Salida ejemplar del código anterior:

<table> <tr><td>Title</td><td>Owner</td><td>Purpose</td></tr> <tr><td>Natuur - Ecologische verbindingszones</td><td>provincie Fryslân</td><td>Beleidsnota "ecologische verbindingszones in Fryslân" vastgesteld door Provinciale Staten op 4 oktober 2006. Opgenomen in het Streekplan 2007</td></tr> <tr><td>CORINE: Veranderingen in landgebruik in Nederland tussen 1986 en 2000.</td><td>Alterra, Wageningen UR</td><td>Het monitoren van landgebruiksveranderingen op Europese schaal volgens een standaard methode.</td></tr> <tr><td>Viswaterkaart Sportvisserij</td><td>Sportvisserij Nederland</td><td>Elke sportvisser moet exact weten waar die onder welke (bijz.) voorwaarden mag hengelen.</td></tr> <tr><td>Veiligheidsafstand vuurwerk</td><td>Interprovinciaal Overleg</td><td>Risicokaart</td></tr> <tr><td>Weggeg convergenties</td><td>Rijkswaterstaat Data en ICT Dienst (RWS DID)</td><td>Ruimtelijke analyses waarbij ligging van infrastructuur van belang is en bereikbaarheidsberekeningen</td></tr> <tr><td>Beheerkaart Nat Versie januari 2008</td><td>Rijkswaterstaat Data en ICT Dienst (RWS DID)</td><td>De Beheerkaart Nat wordt door de natte districten van Rijkswaterstaat gebruikt ten behoeve van beheer en onderhoud van zijn beheerobjecten van de watersystemenen. Het NIS gebruikt de gegevens om ondermeer de benodigde budgetten te bepalen voor beheer en onderhoud.</td></tr> <tr><td>Orthofotomozaieken_project</td><td>Rijkswaterstaat Data en ICT Dienst (RWS DID)</td><td>Gebruik als ondergrond</td></tr> <tr><td>Knelpunten in LAW-routes</td><td>Stichting Wandelnet</td><td>Inventarisatie van knelpunten in LAW-routes voor provincies</td></tr> <tr><td>Electronische zeekaarten Ned. Cont. Plat usage Harbour</td><td>Dienst der Hydrografie</td><td>Veilige navigatie</td></tr> <tr><td>Maatregelzone kernenergie</td><td>Interprovinciaal Overleg</td><td>Risicokaart</td></tr> </table>

En el código anterior utilicé clases desde aquí: https://gist.github.com/hakre/94a36e4587214a6e9bc9