Obtener valor del objeto SimpleXMLElement

197

Tengo algo como esto:

$url = "http://ws.geonames.org/findNearbyPostalCodes?country=pl&placename=";
$url .= rawurlencode($city[$i]);

$xml = simplexml_load_file($url);
echo $url."\n";
$cityCode[] = array(
    'city' => $city[$i], 
    'lat' => $xml->code[0]->lat, 
    'lng' => $xml->code[0]->lng
);

Se supone que descarga XML de geonames. Si lo hago print_r($xml), obtengo:

SimpleXMLElement Object
(
    [code] => Array
        (
            [0] => SimpleXMLElement Object
                (
                    [postalcode] => 01-935
                    [name] => Warszawa
                    [countryCode] => PL
                    [lat] => 52.25
                    [lng] => 21.0
                    [adminCode1] => SimpleXMLElement Object
                        (
                        )

                    [adminName1] => Mazowieckie
                    [adminCode2] => SimpleXMLElement Object
                        (
                        )

                    [adminName2] => Warszawa
                    [adminCode3] => SimpleXMLElement Object
                        (
                        )

                    [adminName3] => SimpleXMLElement Object
                        (
                        )

                    [distance] => 0.0
                )

Hago lo que puedes ver $xml->code[0]->laty devuelve un objeto. ¿Como puedo obtener el valor?

kubas
fuente
@kubas, ¿puedes seleccionar una respuesta?
kqw
1
Actualización 2017: SO ya no muestra la mejor respuesta en la parte superior. La mejor respuesta está aquí .
rinogo
2
@rinogo Probablemente has hecho clic accidentalmente en una de las pestañas de clasificación en la parte superior del bloque de respuesta. La respuesta a la que se vinculó tiene 345 votos, por lo que se muestra en la parte superior si tiene la clasificación establecida en "votos".
IMSoP
1
Gracias, @IMSoP! Tienes razón: debo haber hecho clic en "activo" en algún momento (útil para preguntas antiguas con respuestas desactualizadas, por cierto). ¡Es bueno saber que necesito volver a cambiarlo a "votos"! :)
rinogo

Respuestas:

426

Tienes que lanzar un objeto simpleXML a una cadena.

$value = (string) $xml->code[0]->lat;
Luis Melgratti
fuente
9
Acabo de notar que si json_encode el objeto xml y luego json_decode se obtiene un stdObject anidado con el que lidiar, bastante útil para cuando eres perezoso y trabajas con estructuras simples: D
Louis
1
pregunta tonta, pero ¿no es eso un error? vea php.net/simplexml#95762 ¿por qué no tiene que emitir texto en algunos campos sino en otros?
gcb
77
¡Esta respuesta sigue pateando traseros aún más duro en 2016!
Hirdesh Vishwdewa
44
No puedo creer que esto sea tan complicado. ¿por qué harían una clase llamada "getName" pero no "getValue"? ¿Por qué imprimirían una cadena vacía si la imprimiera en lugar de convertirla manualmente a (cadena)? ¿¿POR QUÉ??
user151496
1
@ user151496 Técnicamente, el reparto de cadenas no le proporciona el "valor", sino el "contenido de texto". Pero sí, un método específicamente nombrado sería más reconocible. Sin embargo, una vez que te acostumbras a esto, en realidad no es más difícil de usar .
IMSoP
91

También puedes usar el método mágico __toString ()

$xml->code[0]->lat->__toString()
sglessard
fuente
55
O sprintfcon el %sformato. O echoy búfer de salida, o o o o ...
hakre
¿Cómo puedo convertir el objeto a XML en PHP? @sglessard
Gema
17

Si sabe que el valor del elemento XML es un número flotante (latitud, longitud, distancia), puede usar (float)

$value = (float) $xml->code[0]->lat;

Además, (int)para el número entero:

$value = (int) $xml->code[0]->distance;
CursosWeb
fuente
15

Si no conoce el valor del elemento XML, puede usar

$value = (string) $xml->code[0]->lat;

if (ctype_digit($value)) {
    // the value is probably an integer because consists only of digits
}

Funciona cuando necesita determinar si el valor es un número, porque (string)siempre devolverá una cadena y is_int($value)devolveráfalse

vladkras
fuente
Cualquier forma de verificar también para el valor booleano que no sea: (string) $ value == 'true' || (cadena) $ valor == 'falso'?
Talisin
12

Para mí es más fácil usar matrices que objetos,

Entonces, convierto un Xml-Object,

$xml = simplexml_load_file('xml_file.xml');    
$json_string = json_encode($xml);    
$result_array = json_decode($json_string, TRUE);
dazzafact
fuente
3
Convertir a JSON y luego volver al mismo contexto es increíblemente ineficiente a menos que tenga mucha prisa. Trabajar con objetos no es difícil, y si realmente prefiere las matrices, debe convertir el objeto en una matriz de forma nativa.
William
Nunca entiendo por qué la gente hace esto: descarta todas las características de SimpleXML, solo para poder escribir en $xml['foo'][0]['bar'][0]['@attributes']['baz']lugar de hacerlo $xml->foo->bar['baz'].
IMSoP
2

Esta es la función que siempre me ha ayudado a convertir los valores relacionados con xml en matriz

function _xml2array ( $xmlObject, $out = array () ){
    foreach ( (array) $xmlObject as $index => $node )
        $out[$index] = ( is_object ( $node ) ) ? _xml2array ( $node ) : $node;

    return $out;
}
pal4life
fuente
Esto arrojará, o romperá, entre otras cosas: cualquier atributo, cualquier cosa en un espacio de nombres, cualquier bloque CDATA. Todo para que pueda perder los métodos de conveniencia que SimpleXML le ofreció en primer lugar.
IMSoP
1

tratar current($xml->code[0]->lat)

devuelve el elemento debajo del puntero actual de la matriz, que es 0, por lo que obtendrá valor

Bendeberia
fuente
1

puede usar el '{}' para acceder a su propiedad, y luego puede hacer lo que desee. Guárdelo o muestre el contenido.

    $varName = $xml->{'key'};

De tu ejemplo, ella es el código

        $filePath = __DIR__ . 'Your path ';
        $fileName = 'YourFilename.xml';

        if (file_exists($filePath . $fileName)) {
            $xml = simplexml_load_file($filePath . $fileName);
            $mainNode = $xml->{'code'};

            $cityArray = array();

            foreach ($mainNode as $key => $data)        {
               $cityArray[..] = $mainNode[$key]['cityCode'];
               ....

            }     

        }
Zakaria Charik
fuente
3
No es necesario usarlo a {}menos que haya caracteres especiales como guiones allí. $xml->{'key'}es solo una forma más fea de escribir $xml->key.
IMSoP
0
header("Content-Type: text/html; charset=utf8");
$url  = simplexml_load_file("http://URI.com");

 foreach ($url->PRODUCT as $product) {  
    foreach($urun->attributes() as $k => $v) {
        echo $k." : ".$v.' <br />';
    }
    echo '<hr/>';
}
Şahin Solmaz
fuente
2
Esta respuesta carece de explicación y, por lo tanto, pierde la idea clave que echoobliga $va convertirse en una cadena en lugar de un objeto, tal como lo (string)$vharía.
IMSoP
0

puedes convertir una matriz con esta función

function xml2array($xml){
$arr = array();

foreach ($xml->children() as $r)
{
    $t = array();
    if(count($r->children()) == 0)
    {
        $arr[$r->getName()] = strval($r);
    }
    else
    {
        $arr[$r->getName()][] = xml2array($r);
    }
}
return $arr;
}
nixis
fuente
Puedes, pero ¿por qué quieres hacerlo?
IMSoP
Porque si el índice es un número, puede ser un problema. ¿Te gusta esta $ variable-> código-> 0? SimpleXMLElement Object ([code] => Array ([0] => SimpleXMLElement Object (...
nixis
<0>no es una etiqueta XML válida, por lo que eso nunca sucedería. Algunos nombres XML válidos no son nombres PHP válidos, como <foo-bar>, pero eso se puede manejar con ->{'foo-bar'}sintaxis. ¿O simplemente te refieres a acceder al 0elemento th en una lista de elementos con nombre similares? Eso es justo $parent->childName[0].
IMSoP
0
$codeZero = null;
foreach ($xml->code->children() as $child) {
   $codeZero = $child;
}

$lat = null;
foreach ($codeZero->children() as $child) {
   if (isset($child->lat)) {
      $lat = $child->lat;
   }
}
estirado
fuente
No estoy seguro de lo que este fragmento de código está tratando de mostrar, pero parece que es una forma muy complicada (e incorrecta) de escribir $codeZero = $xml->code[0]y$lat = $xml->code[0]->lat
IMSoP
@IMSoP, ¿está rechazando esto sin siquiera probar el código? No lo habría publicado si no funcionara. Su sugerencia no permite un foreach, que es más limpio que un simple bucle for. Ya no recuerdo todos los detalles, pero funcionó cuando otras soluciones no lo hicieron.
Craned
Voté en contra principalmente porque no tiene explicación de lo que está haciendo. Si incluso no lo entiendes cuando lo lees de nuevo, ¿cómo se supone que será útil para alguien más? En cuanto a usar un foreach, seguro que puedes: foreach ( $xml->code as $code ) { $lat = (string)$code->lat; echo $lat; }Pero lo que estás haciendo aquí es "todos los hijos del primer <code>elemento"; lo cual no tiene mucho sentido para mí. Para aclarar, $xml->code->children()es la abreviatura de $xml->code[0]->children(), no "niños llamados code".
IMSoP
@IMSoP, bueno, ese es un punto justo. Debería haber proporcionado una explicación; probablemente me pareció tan obvio en ese momento que no se me ocurrió. Sin embargo, a mi favor, al menos, es el hecho de que quien está en esta página ya conoce el contexto y no necesitará una explicación porque ya sabe lo que está sucediendo. Esto se demuestra por el hecho de que una persona ya votó mi respuesta o ahora estaría en negativo. Lo haré mejor publicando una explicación en el futuro.
Craned
Además, casi puedo garantizar que habría intentado su sugerencia primero, y no funcionó, así que tuve que encontrar otra.
Craned
-2
foreach($xml->code as $vals )
{ 
    unset($geonames);
    $vals=(array)$vals;
    foreach($vals as $key => $value)
      {
        $value=(array)$value;
        $geonames[$key]=$value[0];
      }
}
print_r($geonames);
Amal Jo Antony
fuente