PHP codificación json_encode los números como cadenas

142

Tengo un problema con la función PHP json_encode. Codifica los números como cadenas, p. Ej.

array('id' => 3)

se convierte

"{ ["id": "3", ...)

Cuando js encuentra estos valores, los interpreta como cadenas y las operaciones numéricas fallan en ellos. ¿Alguien sabe alguna forma de evitar json_encodecodificar números como cadenas? ¡Gracias!

Chris Barnhill
fuente
Resulta que este es un problema específico de la versión. A veces, una extracción de una base de datos MySql mantendrá los tipos correctos. En versiones anteriores, puede devolver todo como una cadena. Escribí sobre esto esta mañana. shakyshane.com/blog/output-json-from-php.html
shane
1
Estaba teniendo el mismo problema y pude resolver el mío usando los mutadores de Laravel en el modelo. Le permite modificar los valores en el modelo. laravel.com/docs/eloquent#accessors-and-mutators Al principio no lo entendí bien, pero esta pregunta ayudó: stackoverflow.com/questions/16985656/…
Jazzy

Respuestas:

28

He hecho una prueba muy rápida:

$a = array(
    'id' => 152,
    'another' => 'test',
    'ananother' => 456,
);
$json = json_encode($a);
echo $json;

Esto parece ser lo que usted describe, si no me equivoco?

Y estoy obteniendo como salida:

{"id":152,"another":"test","ananother":456}

Entonces, en este caso, los enteros no se han convertido a cadena.


Aún así, esto podría depender de la versión de PHP que estamos usando: se han corregido un par de errores relacionados con json_encode, dependiendo de la versión de PHP ...

Esta prueba se ha realizado con PHP 5.2.6; Estoy obteniendo lo mismo con PHP 5.2.9 y 5.3.0; Sin embargo, no tengo otra versión 5.2.x para probar :-(

¿Qué versión de PHP estás usando? ¿O es su caso de prueba más complejo que el ejemplo que publicó?

¿Quizás un informe de error en http://bugs.php.net/ podría estar relacionado? Por ejemplo, Bug # 40503: ¿la conversión de enteros json_encode es inconsistente con PHP ?


Tal vez el error # 38680 también podría interesarte, por cierto.

Pascal MARTIN
fuente
Gracias martin Estoy usando 5.2.9. Me pregunto si los datos numéricos se leen de la base de datos como una cadena. Estoy seguro de que los tipos de campo son int, pero no puedo pensar en otra explicación. Probaré su prueba rápida en mi sistema y veré si obtengo el mismo resultado.
Chris Barnhill
12
OK sobre 5.2.9; si sus datos provienen de una base de datos, el problema podría estar allí: a menudo he visto datos provenientes de la base de datos con todo encapsulado a una cadena (he visto esto con PDO y mssql; pero, si no recuerdo mal, esto también sucede para MySQL en PHP <5.3, cuando el nuevo controlador mysqlnd aún no existía) ;; para verificar cómo se ven sus datos, puede usar var_dump, que genera los tipos de cada parte de los datos.
Pascal MARTIN
(continuación) ¿cree que el tipo de datos para los valores numéricos es una cadena? ¿Algunas ideas?
Chris Barnhill
1
No sé exactamente la razón técnica de "por qué los datos se devuelven de MySQL como una cadena" ;; probablemente algo que tenga que ver con el controlador entre PHP y MySQL ;; eso es algo que (al menos en algunos casos) está corregido por el nuevo controlador mysqlnd que viene con PHP 5.3 (vea blog.ulf-wendel.de/?p=184 ; busque "integer" en la página para encontrar lo interesante frase) ;; pero estoy de acuerdo en que esto no es bueno ^^
Pascal MARTIN
No importa que los datos numéricos devueltos por json_encode () no estén entre comillas, siguen siendo una cadena. El valor de retorno de la función json_encode () es una cadena. Con respecto a que MySQL devuelve todos los campos como cadenas, sí, también he encontrado esto específicamente con PDO. A mi modo de ver, siempre debe emitir valores que espera que sean numéricos a enteros (o flotantes) en PHP para asegurarse, no confíe en MySQL o en cualquier otra base de datos para devolver valores con el tipo correcto.
Richard Knop, el
352

Tenga en cuenta que desde PHP 5.3.3, hay un indicador para los números de conversión automática (el parámetro de opciones se agregó en PHP 5.3.0):

$arr = array( 'row_id' => '1', 'name' => 'George' );
echo json_encode( $arr, JSON_NUMERIC_CHECK ); // {"row_id":1,"name":"George"}
Rijk
fuente
44
Tenga en cuenta que JSON_NUMERIC_CHECK requiere PHP 5.3.3.
Robert
10
Funcionó perfectamente hasta que emitió una etiqueta numérica a un número entero, explotando .toLowerCase () en IE. Tenga cuidado, esta solución es simple pero demasiado entusiasta.
Brad Koch
66
ERES MI HÉROE, me encanta.
Petrogad
55
Tiene algún efecto secundario si su cadena no es un número sino un contenido como este: 5252788e16597. referencia: bugs.php.net/bug.php?id=64695
TonyQ
20
JSON_NUMERIC_CHECKintenta adivinar automáticamente si una cadena es un número o no al intentar analizarla. Eso es bastante poco confiable si lo piensas. Convertirá todas las propiedades de aspecto numérico en números (no solo las que desee) y lo hará solo si parecen números. Eso es al menos inestable si no inseguro. El código que consume el JSON producido podría depender de que el tipo sea uno u otro. Cosas extrañas pueden suceder si esas expectativas no se cumplen. Si le interesan las buenas prácticas y la seguridad, debe convertir selectivamente los valores que desee.
wadim
35

Yo, también estaba leyendo desde un DB (PostgreSQL) y todo era una cadena. Recorremos cada fila y hacemos cosas con ella para construir nuestra matriz de resultados finales, así que utilicé

$result_arr[] = array($db_row['name'], (int)$db_row['count']);

dentro del bucle para forzarlo a ser un valor entero. Cuando lo hago json_encode($result_arr)ahora, lo formatea correctamente como un número. Esto le permite controlar lo que es y no es un número proveniente de su base de datos.

EDITAR:

los json_encode() función también tiene la capacidad de hacer esto sobre la marcha utilizando la JSON_NUMERIC_CHECKbandera como segundo argumento. Sin embargo, debe tener cuidado al usarlo, como se muestra en este ejemplo de usuarios en la documentación (copiada a continuación): http://uk3.php.net/manual/en/function.json-encode.php#106641

<?php
// International phone number
json_encode(array('phone_number' => '+33123456789'), JSON_NUMERIC_CHECK);
?>

Y luego obtienes este JSON:

{"phone_number":33123456789}
mouckatron
fuente
sí, parece que el problema es del adaptador DB que no interpreta los tipos de datos, NO de la json_encodefunción. esta es la respuesta más correcta, pero tenga cuidado ya que JSON_NUMERIC_CHECKtambién convierte números de teléfono y otros valores de cadena numéricos, y esto podría dar problemas en ceros a la izquierda o '+' ... Sugiero corregir este problema en la función de lectura de DB.
caesarsol el
7

Me encuentro con el mismo problema (PHP-5.2.11 / Windows). Estoy usando esta solución

$json = preg_replace( "/\"(\d+)\"/", '$1', $json );

que reemplaza todos los números (no negativos, enteros) entre comillas con el número mismo ('"42"' se convierte en '42').

Vea también este comentario en el manual de PHP .

oli_arborum
fuente
Gracias por el código, pero lamentablemente no funcionó en mi json porque tengo un número como nombre de objeto, y parece que invalida el json :(
SSH este
@SSH Esto, tal vez deberías usar esta sintaxis para convertir la matriz en una matriz codificada JSON y no en un objeto. $json_array = json_encode($some_array, false);Entonces el argumento falso le dice a PHP que no haga la conversión de objetos.
hyde
No es seguro utilizar esa solución alternativa. Obtendrá json inválido con estructuras como esa:json_encode(array(-1=>'que', '0'=>'-1'))
Alex Yaroshevich
Tuve un problema al revés, necesitaba mis enteros codificados como cadenas en PHP 7.0 y usé esto$this->data = preg_replace("/\" *?: *?(\d+)/", '":"$1"', $this->data);
Maciej Swic
Cambiaré la expresión regular original a `" /\"(\d+\.?\d*)\"/ "` para incluir decimales, otra nota es que los hombres que usan JSON_NUMERIC_CHECK enfrentarán el problema cuando una cadena también es correcta número en notación científica. por ejemplo, 19E008. JSON_NUMERIC_CHECK lo convertirá a 190000 ...
Sahib Khan
3

La siguiente prueba confirma que cambiar el tipo a cadena hace que json_encode () devuelva un valor numérico como una cadena JSON (es decir, rodeado de comillas dobles). Use settype (arr ["var"], "integer") o settype ($ arr ["var"], "float") para arreglarlo.

<?php

class testclass {
    public $foo = 1;
    public $bar = 2;
    public $baz = "Hello, world";
}

$testarr = array( 'foo' => 1, 'bar' => 2, 'baz' => 'Hello, world');

$json_obj_txt = json_encode(new testclass());
$json_arr_txt = json_encode($testarr);

echo "<p>Object encoding:</p><pre>" . $json_obj_txt . "</pre>";
echo "<p>Array encoding:</p><pre>" . $json_arr_txt . "</pre>";

// Both above return ints as ints. Type the int to a string, though, and...
settype($testarr["foo"], "string");
$json_arr_cast_txt = json_encode($testarr);
echo "<p>Array encoding w/ cast:</p><pre>" . $json_arr_cast_txt . "</pre>";

?>
Jay Andrew Allen
fuente
2

En aras de la exhaustividad (ya que todavía no puedo agregar comentarios), permítanme también agregar este detalle como otra respuesta:

(Editar: para leer después de darse cuenta de que los datos de origen (es decir, en el caso del OP, conjunto de resultados de la base de datos) podrían ser el problema (devolviendo columnas numéricas como cadenas), y json_encode () de hecho no fue el origen del problema)

Páginas del manual de ambos " mysql_fetch_array ":

Devuelve una matriz de cadenas que corresponde a la fila obtenida,

... y " mysql_ fetch_ row ":

Devuelve una matriz numérica de cadenas que corresponde a la fila recuperada

establece claramente que; Las entradas en la matriz devuelta serán cadenas.

(Estaba usando la clase DB en phpBB2 (sí, lo sé, ¡está obsoleto!), Y el método "sql_fetchrow ()" de esa clase usa "mysql_fetch_array ()")

Sin darme cuenta, ¡también terminé encontrando esta pregunta y entendiendo el problema! :)

Como Pascal Martin declaró anteriormente en sus comentarios de seguimiento, creo que una solución que se ocupa del problema del "tipo incorrecto" en la fuente (es decir, mediante el uso de la función " mysql_field_type () " y haciendo el casting justo después de buscar, (o otros métodos de búsqueda como "objeto"?)) serían los mejores en general.

OzgurH
fuente
2

Entonces Pascal MARTIN no está recibiendo suficiente crédito aquí. Comprobar los valores numéricos en cada declaración JSON no es factible para un proyecto existente con cientos de funciones del lado del servidor.

Reemplacé php-mysql con php-mysqlnd, y el problema desapareció. Los números son números, las cadenas son cadenas, los booleanos son booleanos.

Rory Jarrard
fuente
0

También tuve el mismo problema al procesar datos de la base de datos. Básicamente, el problema es que el tipo en la matriz para convertir en json es reconocido por PHP como una cadena y no como un entero. En mi caso, realicé una consulta que devuelve datos de una fila de conteo de columnas DB. El controlador PDO no reconoce la columna como int, sino como cadenas. Lo resolví realizando un reparto como int en la columna afectada.

balucio
fuente
0
$rows = array();
while($r = mysql_fetch_assoc($result)) {
    $r["id"] = intval($r["id"]); 
    $rows[] = $r;
}
print json_encode($rows);  
Yar
fuente
0

es la versión de php el problema, tuve el mismo problema actualizado mi versión de php a 5.6 resolvió el problema

Peter Allen
fuente
0

La conversión de los valores a un int o flotante parece solucionarlo. Por ejemplo:

$coordinates => array( 
    (float) $ap->latitude,
    (float) $ap->longitude 
);
Derrick Miller
fuente
0

¡Puede usar (int) si ocurre algún problema! Funcionará bien

Rahul Gupta
fuente
-1

Simplemente me encontré con el mismo problema y la base de datos devolvió los valores como cadenas.

Lo uso como una solución alternativa:

$a = array(
    'id' => $row['id'] * 1,
    'another' => ...,
    'ananother' => ...,
);
$json = json_encode($a);

Eso es multiplicar el valor por 1 para convertirlo en un número

Espero que ayude a alguien

León
fuente
Usar un multiplicador no es la solución más eficiente. considere usar JSON_NUMERIC_CHECK en json_encode ya que lo arreglará automáticamente
Erick
-2

json_encode serializa alguna estructura de datos en formato JSON para enviarla a través de la red. Por lo tanto, todo el contenido será del tipo cadena. Al igual que cuando recibe algún parámetro de $ _POST o $ _GET.

Si tiene que realizar operaciones numéricas en los valores enviados, simplemente conviértalos a int primero (con la función intval () en PHP o parseInt () en Javascript) y luego ejecute las operaciones.

rogeriopvl
fuente
eso no es de lo que está hablando. está hablando de que el json tiene comillas dobles alrededor de los números.
JasonWoof
En el caso de JSON, conserva los tipos. Imagine escribir un objeto JavaScript literal, cualquier valor entre comillas se convierte en cadenas, pero los números no entre comillas se convierten en enteros, 0x [0-9a-z] se convierte en hexadecimal, etc. Hay diferencias de tipo con PHP, como por ejemplo, no existe una matriz asociativa, simplemente objetos o matrices indexadas etc.
bucabay
Derecha. El problema que tenía era que tenía una variable php, que creía que tenía tipo int, porque provenía de una columna DB de tipo int. Pero, de hecho, la variable PHP tenía cadena de tipo, por lo tanto, las comillas en el JSON.
JasonWoof
-2

Bueno, PHP json_encode () devuelve una cadena.

Sin embargo, puede usar parseFloat () o parseInt () en el código js:

parseFloat('122.5'); // returns 122.5
parseInt('22'); // returns 22
parseInt('22.5'); // returns 22
Richard Knop
fuente
Gracias. Esperaba que hubiera un método más elegante.
Chris Barnhill
1
Sí, la forma más segura es analizarlos. pero, de nuevo, ¿no se escribe Javascript libremente?
mauris
Solo un comentario: está escrito de forma holgada, pero el resultado de "1" +1 será ... 11 - por lo que usar JS debería ser más atento que en lenguajes de tipo fuerte, porque le advertirán, JS solo hace lo que es pensar es mejor si se manejan números de cadena ...
SamiSalami
Sí, pero ese no es el punto, json_encode no debe agregar comillas a los campos numéricos.
andreszs
-3

Como dijo oli_arborum, creo que puedes usar a preg_replacepara hacer el trabajo. Simplemente cambie el comando así:

$json = preg_replace('#:"(\d+)"#', ':$1', $json);
Santini Arnaud
fuente