PHP json_encode encodant les nombres sous forme de chaînes

142

J'ai un problème avec la fonction PHP json_encode. Il encode les nombres sous forme de chaînes, par exemple

array('id' => 3)

devient

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

Lorsque js rencontre ces valeurs, il les interprète comme des chaînes et les opérations numériques échouent sur elles. Est-ce que quelqu'un connaît un moyen d'empêcher json_encoded'encoder des nombres sous forme de chaînes? Je vous remercie!

Chris Barnhill
la source
Il s'avère que c'est un problème spécifique à la version. Parfois, une extraction d'une base de données MySql conservera les types corrects. Dans les anciennes versions, il peut tout renvoyer sous forme de chaîne. J'ai écrit à ce sujet ce matin. shakyshane.com/blog/output-json-from-php.html
shane
1
J'avais le même problème et j'ai pu résoudre le mien en utilisant Mutators in the Model de Laravel. Il vous permet de modifier les valeurs du modèle. laravel.com/docs/eloquent#accessors-and-mutators Je ne l'ai pas tout à fait compris au début, mais cette question a aidé: stackoverflow.com/questions/16985656/…
Jazzy

Réponses:

28

J'ai fait un test très rapide:

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

Cela semble être comme ce que vous décrivez, si je ne me trompe pas?

Et je reçois en sortie:

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

Donc, dans ce cas, les entiers n'ont pas été convertis en chaîne.


Pourtant, cela peut dépendre de la version de PHP que nous utilisons: il y a eu quelques bogues liés à json_encode corrigés, en fonction de la version de PHP ...

Ce test a été réalisé avec PHP 5.2.6; J'obtiens la même chose avec PHP 5.2.9 et 5.3.0; Je n'ai pas d'autre version 5.2.x à tester, cependant :-(

Quelle version de PHP utilisez-vous? Ou votre cas de test est-il plus complexe que l'exemple que vous avez publié?

Peut-être qu'un rapport de bogue sur http://bugs.php.net/ pourrait être lié? Par exemple, bogue n ° 40503: la conversion d'entiers json_encode est incompatible avec PHP ?


Peut-être que le bogue n ° 38680 pourrait vous intéresser aussi, btw?

Pascal MARTIN
la source
Merci Martin. J'utilise 5.2.9. Je me demande si les données numériques sont lues à partir de la base de données sous forme de chaîne? Je suis sûr que les types de champs sont int, mais je ne peux pas penser à une autre explication. Je vais essayer votre test rapide sur mon système et voir si j'obtiens le même résultat.
Chris Barnhill
12
OK environ 5,2,9; si vos données proviennent d'une base de données, le problème est peut-être là: j'ai souvent vu des données provenir d'une base de données avec tout ce qui était casté en chaîne (j'ai vu cela avec PDO et mssql; mais, si je me souviens bien, cela se produit aussi pour MySQL en PHP <5.3, lorsque le nouveau pilote mysqlnd n'existait pas encore) ;; pour vérifier à quoi ressemblent vos données, vous pouvez utiliser var_dump, qui génère les types de chaque partie des données.
Pascal MARTIN
(suite) pense que le type de données pour les valeurs numériques est chaîne? Des idées?
Chris Barnhill
1
Je ne connais pas exactement la raison technique de "pourquoi les données sont renvoyées de MySQL sous forme de chaîne" ;; probablement quelque chose qui a à voir avec le pilote entre PHP et MySQL ;; c'est quelque chose qui est (au moins dans certains cas) correct par le nouveau pilote mysqlnd fourni avec PHP 5.3 (voir blog.ulf-wendel.de/?p=184 ; recherchez "integer" dans la page pour trouver l'intéressant phrase) ;; mais je suis d'accord que ce n'est pas sympa ^^
Pascal MARTIN
Peu importe que les données numériques renvoyées par json_encode () ne soient pas entre guillemets, il s'agit toujours d'une chaîne. La valeur de retour de la fonction json_encode () est une chaîne. En ce qui concerne MySQL renvoyant tous les champs sous forme de chaînes, oui, j'ai également rencontré cela spécifiquement avec PDO. De la façon dont je vois les choses, vous devez toujours convertir les valeurs que vous attendez d'être numériques en entier (ou flottant) en PHP pour vous assurer de ne pas faire confiance à MySQL ou à toute autre base de données pour renvoyer des valeurs de type correct.
Richard Knop
352

Notez que depuis PHP 5.3.3, il existe un indicateur pour la conversion automatique des nombres (le paramètre options a été ajouté dans PHP 5.3.0):

$arr = array( 'row_id' => '1', 'name' => 'George' );
echo json_encode( $arr, JSON_NUMERIC_CHECK ); // {"row_id":1,"name":"George"}
Rijk
la source
4
Notez que JSON_NUMERIC_CHECK nécessite PHP 5.3.3.
Robert
10
A fonctionné parfaitement jusqu'à ce qu'il convertisse une étiquette numérique en un entier, faisant exploser .toLowerCase () dans IE. Attention, cette solution est simple mais trop zélée.
Brad Koch du
6
VOUS ÊTES MON HÉROS, j'adore.
Petrogad
5
Cela a un effet secondaire si votre chaîne n'est pas un nombre mais un contenu comme celui-ci: 5252788e16597. référence: bugs.php.net/bug.php?id=64695
TonyQ
20
JSON_NUMERIC_CHECKtente de deviner automatiquement si une chaîne est un nombre ou non en essayant de l'analyser. C'est assez peu fiable si vous y réfléchissez. Il convertira toutes les propriétés numériques en nombres (pas seulement celles que vous voulez) et cela ne le fera que si elles ressemblent à des nombres. C'est au moins fragile sinon dangereux. Le code qui consomme le JSON produit peut dépendre du type étant l'un ou l'autre. Des choses étranges peuvent arriver si ces attentes ne sont pas satisfaites. Si vous vous souciez des bonnes pratiques et de la sécurité, vous devez convertir sélectivement les valeurs souhaitées.
wadim
35

Moi aussi je lisais à partir d'une base de données (PostgreSQL) et tout était une chaîne. Nous bouclons sur chaque ligne et faisons des choses avec pour construire notre tableau de résultats finaux, alors j'ai utilisé

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

dans la boucle pour le forcer à être une valeur entière. Quand je le fais json_encode($result_arr)maintenant, il le formate correctement sous forme de nombre. Cela vous permet de contrôler ce qui est et n'est pas un nombre provenant de votre base de données.

ÉDITER:

La json_encode()fonction a également la capacité de le faire à la volée en utilisant le JSON_NUMERIC_CHECKdrapeau comme deuxième argument. Vous devez cependant faire attention à l'utiliser comme indiqué dans cet exemple utilisateur dans la documentation (copiée ci-dessous): http://uk3.php.net/manual/en/function.json-encode.php#106641

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

Et puis vous obtenez ce JSON:

{"phone_number":33123456789}
mouckatron
la source
oui, il semble que le problème vient de l'adaptateur de base de données qui n'interprète pas les types de données, PAS de la json_encodefonction. c'est la réponse la plus correcte, mais soyez prudent car JSON_NUMERIC_CHECKconvertit également les numéros de téléphone et autres valeurs de chaînes numériques, et cela pourrait poser des problèmes dans les zéros de tête ou '+' ... Je suggère de corriger ce problème dans la fonction de lecture de la base de données.
caesarsol
8

essayer $arr = array('var1' => 100, 'var2' => 200);
$json = json_encode( $arr, JSON_NUMERIC_CHECK);

Mais cela ne fonctionne que sur PHP 5.3.3. Regardez ce journal des modifications PHP json_encode http://php.net/manual/en/function.json-encode.php#refsect1-function.json-encode-changelog

habibillah
la source
Cela a fonctionné pour moi. Installation par défaut de PHP + apache sur debian squeeze 6.0.5 avec des données provenant de postgre db
Nikolay Spassov
7

Je rencontre le même problème (PHP-5.2.11 / Windows). J'utilise cette solution de contournement

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

qui remplace tous les nombres (non négatifs, entiers) entre guillemets par le nombre lui-même («42» devient «42»).

Voir aussi ce commentaire dans le manuel PHP .

oli_arborum
la source
Merci pour le code, mais malheureusement cela n'a pas fonctionné sur mon json car j'ai un numéro comme nom d'objet, et il semble rendre le json invalide :(
SSH This
@SSHCeci, vous devriez peut-être utiliser cette syntaxe pour convertir le tableau en un tableau encodé JSON et non en un objet. $json_array = json_encode($some_array, false);Ainsi, le faux argument indique à PHP de ne pas effectuer la conversion d'objet.
hyde
Il n'est pas du tout sûr d'utiliser cette solution de contournement. Vous obtiendrez des json invalides avec des structures comme celle-ci:json_encode(array(-1=>'que', '0'=>'-1'))
Alex Yaroshevich
J'ai eu un problème dans l'autre sens, j'avais besoin de mes entiers encodés sous forme de chaînes en PHP 7.0 et je l'ai utilisé$this->data = preg_replace("/\" *?: *?(\d+)/", '":"$1"', $this->data);
Maciej Swic
Je vais changer l'expression régulière d'origine en `` /\"(\d+\.?\d*)\"/ "` `pour inclure des décimales, une autre note est que les gars qui utilisent JSON_NUMERIC_CHECK seront confrontés au problème lorsqu'une chaîne est également correcte nombre en notation scientifique. par exemple, 19E008. JSON_NUMERIC_CHECK le convertira en 190000 ...
Sahib Khan
3

Le test suivant confirme que le changement du type en chaîne fait que json_encode () renvoie un numérique sous forme de chaîne JSON (c'est-à-dire entouré de guillemets doubles). Utilisez settype (arr ["var"], "integer") ou settype ($ arr ["var"], "float") pour le corriger.

<?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
la source
2

Par souci d'exhaustivité (car je ne peux pas encore ajouter de commentaires), permettez-moi également d'ajouter ce détail comme une autre réponse:

(Edit: A lire après s'être rendu compte que les données source (c'est-à-dire dans le cas de l'OP, l'ensemble de résultats de la base de données) pourraient être le problème (en renvoyant des colonnes numériques sous forme de chaînes), et json_encode () en fait n'était pas la source du problème)

Pages de manuel des deux " mysql_fetch_array ":

Renvoie un tableau de chaînes qui correspond à la ligne récupérée,

... et " mysql_ fetch_ row ":

Renvoie un tableau numérique de chaînes correspondant à la ligne extraite

indique clairement que; les entrées du tableau renvoyé seront des chaînes.

(J'utilisais la classe DB dans phpBB2 (oui je sais, c'est obsolète!), Et la méthode "sql_fetchrow ()" de cette classe utilise "mysql_fetch_array ()")

Sans m'en rendre compte, j'ai aussi fini par trouver cette question, et comprendre le problème! :)

Comme Pascal Martin l'a déclaré ci-dessus dans ses commentaires de suivi, je crois qu'une solution qui prend en charge le problème du "type incorrect" à la source (c'est-à-dire en utilisant la fonction " mysql_field_type () " et en faisant le casting juste après fetch, (ou d'autres méthodes de récupération comme "objet"?)) seraient les meilleures en général.

OzgurH
la source
2

Donc Pascal MARTIN n'obtient pas assez de crédit ici. La vérification des valeurs numériques à chaque retour JSON n'est pas possible pour un projet existant avec des centaines de fonctions côté serveur.

J'ai remplacé php-mysql par php-mysqlnd, et le problème a disparu. Les nombres sont des nombres, les chaînes sont des chaînes, les booléens sont des booléens.

Rory Jarrard
la source
0

J'ai également eu le même problème de traitement des données de la base de données. Fondamentalement, le problème est que le type du tableau à convertir en json, est reconnu par PHP comme une chaîne et non comme un entier. Dans mon cas, j'ai fait une requête qui renvoie des données à partir d'une ligne de comptage de colonne DB. Le pilote PDO ne reconnaît pas la colonne comme int, mais comme chaînes. J'ai résolu en effectuant un cast comme int dans la colonne affectée.

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

c'est la version php le problème, le même problème a été mis à niveau ma version php à 5.6 a résolu le problème

Peter Allen
la source
0

Le cast des valeurs en int ou float semble résoudre ce problème. Par exemple:

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

Vous pouvez utiliser (int) si un problème survient !! Cela fonctionnera très bien.

Rahul Gupta
la source
-1

Il suffit de rencontrer le même problème et la base de données renvoyait les valeurs sous forme de chaînes.

J'utilise ceci comme solution de contournement:

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

C'est multiplier la valeur par 1 pour la convertir en nombre

J'espère que ça aide quelqu'un

Léon
la source
l'utilisation d'un multiplicateur n'est pas la solution la plus efficace. envisagez d'utiliser JSON_NUMERIC_CHECK sur json_encode car il le corrigera automatiquement
Erick
-2

json_encode sérialise une structure de données au format JSON à envoyer sur le réseau. Par conséquent, tout le contenu sera de type chaîne. Tout comme lorsque vous recevez un paramètre de $ _POST ou $ _GET.

Si vous devez effectuer des opérations numériques sur les valeurs envoyées, convertissez-les d'abord en int (avec la fonction intval () en PHP ou parseInt () en Javascript) puis exécutez les opérations.

rogeriopvl
la source
ce n'est pas ce dont il parle. il parle du json ayant des guillemets doubles autour des nombres.
JasonWoof
Dans le cas de JSON, il conserve les types. Imaginez que vous écrivez un objet JavaScript littéral, toutes les valeurs entre guillemets deviennent des chaînes, mais les nombres non entre guillemets deviennent des entiers, 0x [0-9a-z] devient hexadécimal, etc. Il y a des différences de type avec PHP, comme, un tableau associatif, juste des objets ou des tableaux indexés, etc.
bucabay
droite. Le problème qu'il avait, c'est qu'il avait une variable php, qu'il pensait avoir du type int, parce qu'elle provenait d'une colonne DB de type int. Mais en fait, la variable PHP avait une chaîne de type, donc les guillemets dans le JSON.
JasonWoof
-2

Eh bien, PHP json_encode () renvoie une chaîne.

Vous pouvez cependant utiliser parseFloat () ou parseInt () dans le code js:

parseFloat('122.5'); // returns 122.5
parseInt('22'); // returns 22
parseInt('22.5'); // returns 22
Richard Knop
la source
Merci. J'espérais qu'il y avait une méthode plus élégante.
Chris Barnhill
1
ouais, le moyen le plus sûr est de les analyser. mais encore une fois, Javascript n'est-il pas mal typé?
mauris
Juste un commentaire: il est mal tapé, le résultat de "1" +1 sera toujours ... 11 - donc en utilisant JS vous devriez être plus attentif que dans les langages de type fort, car ils vous avertiront, JS fait juste ce que c'est Je pense qu'il est préférable de gérer les nombres de chaînes ...
SamiSalami
Oui mais ce n'est pas le but, json_encode ne doit pas ajouter de guillemets aux champs numériques.
andreszs
-3

Comme oli_arborum l'a dit, je pense que vous pouvez utiliser un preg_replacepour faire le travail. Changez simplement la commande comme ceci:

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