PHP json_encode codiert Zahlen als Zeichenfolgen

140

Ich habe ein Problem mit der PHP-Funktion json_encode. Es codiert Zahlen als Zeichenfolgen, z

array('id' => 3)

wird

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

Wenn js auf diese Werte stößt, werden sie als Zeichenfolgen interpretiert und numerische Operationen schlagen fehl. Kennt jemand eine Möglichkeit, um zu verhindern json_encode, dass Zahlen als Zeichenfolgen codiert werden? Danke dir!

Chris Barnhill
quelle
Es stellt sich heraus, dass dies ein versionsspezifisches Problem ist. Manchmal werden beim Abrufen aus einer MySQL-Datenbank die richtigen Typen beibehalten. In älteren Versionen wird möglicherweise alles als Zeichenfolge zurückgegeben. Ich habe heute Morgen darüber geschrieben. shakyshane.com/blog/output-json-from-php.html
shane
1
Ich hatte das gleiche Problem und konnte mein Problem mithilfe von Laravels Mutatoren im Modell lösen. Hiermit können Sie die Werte im Modell ändern. laravel.com/docs/eloquent#accessors-and-mutators Ich habe es zuerst nicht ganz verstanden, aber diese Frage hat geholfen: stackoverflow.com/questions/16985656/…
Jazzy

Antworten:

28

Ich habe einen sehr schnellen Test gemacht:

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

Dies scheint so zu sein, wie Sie es beschreiben, wenn ich mich nicht irre?

Und ich bekomme als Ausgabe:

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

In diesem Fall wurden die Ganzzahlen also nicht in Zeichenfolgen konvertiert.


Dies hängt jedoch möglicherweise von der von uns verwendeten PHP-Version ab: Abhängig von der PHP-Version wurden einige Fehler im Zusammenhang mit json_encode behoben ...

Dieser Test wurde mit PHP 5.2.6 durchgeführt; Ich bekomme das gleiche mit PHP 5.2.9 und 5.3.0; Ich habe jedoch keine andere 5.2.x-Version zum Testen :-(

Welche PHP-Version verwenden Sie? Oder ist Ihr Testfall komplexer als das von Ihnen veröffentlichte Beispiel?

Vielleicht könnte ein Fehlerbericht auf http://bugs.php.net/ verwandt sein? Zum Beispiel Fehler # 40503: Die Ganzzahlkonvertierung von json_encode ist nicht mit PHP vereinbar .


Vielleicht könnte Sie auch Bug # 38680 interessieren?

Pascal MARTIN
quelle
Danke Martin. Ich benutze 5.2.9. Ich frage mich, ob die numerischen Daten als Zeichenfolge aus der Datenbank gelesen werden. Ich bin sicher, dass die Feldtypen int sind, aber mir fällt keine andere Erklärung ein. Ich werde Ihren Schnelltest auf meinem System versuchen und sehen, ob ich das gleiche Ergebnis erhalte.
Chris Barnhill
12
OK ungefähr 5.2.9; Wenn Ihre Daten aus einer Datenbank stammen, liegt möglicherweise das Problem vor: Ich habe oft gesehen, dass Daten aus einer Datenbank stammen, bei denen alles in Zeichenfolgen umgewandelt wurde (ich habe dies mit PDO und mssql gesehen; aber wenn ich mich richtig erinnere, passiert dies auch für MySQL in PHP <5.3, als der neue mysqlnd-Treiber noch nicht vorhanden war) ;; Um zu überprüfen, wie Ihre Daten aussehen, können Sie var_dump verwenden, das die Typen der einzelnen Teile der Daten ausgibt.
Pascal MARTIN
(Fortsetzung) denkt, der Datentyp für numerische Werte ist Zeichenfolge? Irgendwelche Ideen?
Chris Barnhill
1
Ich kenne den technischen Grund nicht genau, "warum die Daten von MySQL als Zeichenfolge zurückgegeben werden" ;; wahrscheinlich etwas, das mit dem Treiber zwischen PHP und MySQL zu tun hat ;; Dies wird (zumindest in einigen Fällen) durch den neuen mysqlnd-Treiber korrigiert, der mit PHP 5.3 geliefert wird (siehe blog.ulf-wendel.de/?p=184 ; suchen Sie auf der Seite nach "integer", um das Interessante zu finden Satz) ;; aber ich stimme zu, dass das nicht schön ist ^^
Pascal MARTIN
Es spielt keine Rolle, dass die von json_encode () zurückgegebenen numerischen Daten nicht in Anführungszeichen stehen, sondern immer noch eine Zeichenfolge sind. Der Rückgabewert der Funktion json_encode () ist eine Zeichenfolge. In Bezug auf MySQL, das alle Felder als Zeichenfolgen zurückgibt, habe ich dies auch speziell bei PDO festgestellt. So wie ich es sehe, sollten Sie in PHP immer Werte umwandeln, von denen Sie erwarten, dass sie numerisch bis ganzzahlig (oder float) sind, um sicherzustellen, dass Sie MySQL oder einer anderen Datenbank nicht vertrauen, um Werte mit dem richtigen Typ zurückzugeben.
Richard Knop
348

Beachten Sie, dass es seit PHP 5.3.3 ein Flag für die automatische Konvertierung von Zahlen gibt (der Parameter options wurde in PHP 5.3.0 hinzugefügt):

$arr = array( 'row_id' => '1', 'name' => 'George' );
echo json_encode( $arr, JSON_NUMERIC_CHECK ); // {"row_id":1,"name":"George"}
Rijk
quelle
4
Beachten Sie, dass für JSON_NUMERIC_CHECK PHP 5.3.3 erforderlich ist.
Robert
10
Funktionierte perfekt, bis eine numerische Beschriftung in eine Ganzzahl umgewandelt und .toLowerCase () im IE in die Luft gesprengt wurde. Seien Sie vorsichtig, diese Lösung ist einfach, aber übereifrig.
Brad Koch
6
Du bist mein Held, liebe es.
Petrogad
5
Es hat einige Nebenwirkungen, wenn Ihre Zeichenfolge keine Zahl, sondern ein Inhalt wie der folgende ist: 5252788e16597. Referenz: bugs.php.net/bug.php?id=64695
TonyQ
20
JSON_NUMERIC_CHECKversucht automatisch zu erraten, ob eine Zeichenfolge eine Zahl ist oder nicht, indem versucht wird, sie zu analysieren. Das ist ziemlich unzuverlässig, wenn man darüber nachdenkt. Es konvertiert alle numerisch aussehenden Eigenschaften in Zahlen (nicht nur die gewünschten) und dies nur, wenn sie wie Zahlen aussehen. Das ist zumindest wackelig, wenn nicht unsicher. Der Code, der den erzeugten JSON verwendet, hängt möglicherweise vom Typ ab. Seltsame Dinge können passieren, wenn diese Erwartungen nicht erfüllt werden. Wenn Sie sich für bewährte Verfahren und Sicherheit interessieren, sollten Sie die gewünschten Werte selektiv konvertieren.
Wadim
35

Ich habe ebenfalls aus einer DB (PostgreSQL) gelesen und alles war ein String. Wir durchlaufen jede Zeile und machen Dinge damit, um unser endgültiges Ergebnisarray aufzubauen, also habe ich es verwendet

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

innerhalb der Schleife, um zu erzwingen, dass es sich um einen ganzzahligen Wert handelt. Wenn ich es json_encode($result_arr)jetzt mache , formatiert es es korrekt als Zahl. Auf diese Weise können Sie steuern, welche Nummer aus Ihrer Datenbank stammt und welche nicht.

BEARBEITEN:

Die json_encode()Funktion kann dies auch im laufenden Betrieb tun, indem sie das JSON_NUMERIC_CHECKFlag als zweites Argument verwendet. Sie müssen jedoch vorsichtig damit umgehen, wie in diesem Benutzerbeispiel in der Dokumentation (unten kopiert) gezeigt: http://uk3.php.net/manual/en/function.json-encode.php#106641

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

Und dann bekommen Sie diesen JSON:

{"phone_number":33123456789}
Mouckatron
quelle
Ja, anscheinend liegt das Problem beim DB-Adapter, der die Datentypen nicht interpretiert, NICHT bei der json_encodeFunktion. Dies ist die richtigste Antwort, aber seien Sie vorsichtig, da JSON_NUMERIC_CHECKauch Telefonnummern und andere numerische Zeichenfolgenwerte konvertiert werden. Dies kann zu Problemen bei führenden Nullen oder '+' führen. Ich empfehle, dieses Problem in der DB-Lesefunktion zu beheben.
Caesarsol
8

Ich habe das gleiche Problem (PHP-5.2.11 / Windows). Ich verwende diese Problemumgehung

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

Dies ersetzt alle (nicht negativen, ganzzahligen) Zahlen in Anführungszeichen durch die Zahl selbst ("42" wird zu "42").

Siehe auch diesen Kommentar im PHP-Handbuch .

oli_arborum
quelle
Vielen Dank für den Code, aber leider hat es auf meinem json nicht funktioniert, weil ich eine Nummer als Objektname habe, und es scheint den json ungültig zu machen :(
SSH This
@SSHThis, vielleicht sollten Sie diese Syntax verwenden, um das Array in ein JSON-codiertes Array und nicht in ein Objekt zu konvertieren. $json_array = json_encode($some_array, false);Das falsche Argument weist PHP an, die Objektkonvertierung nicht durchzuführen.
Hyde
Es ist überhaupt nicht sicher, diese Problemumgehung zu verwenden. Sie erhalten ungültigen json mit Strukturen wie diesen:json_encode(array(-1=>'que', '0'=>'-1'))
Alex Yaroshevich
Ich hatte ein Problem umgekehrt, ich brauchte meine ganzen Zahlen, die in PHP 7.0 als Strings codiert waren, und benutzte diese$this->data = preg_replace("/\" *?: *?(\d+)/", '":"$1"', $this->data);
Maciej Swic
Ich werde den ursprünglichen regulären Ausdruck in "/\"(\d+\.?\d*)\"/" ändern, um Dezimalstellen einzuschließen. Ein weiterer Hinweis ist, dass Leute, die JSON_NUMERIC_CHECK verwenden, mit dem Problem konfrontiert werden, wenn eine Zeichenfolge ebenfalls korrekt ist Nummer in wissenschaftlicher Notation. zB 19E008. JSON_NUMERIC_CHECK wird es in 190000 konvertieren ...
Sahib Khan
3

Der folgende Test bestätigt, dass das Ändern des Typs in eine Zeichenfolge dazu führt, dass json_encode () eine Zahl als JSON-Zeichenfolge zurückgibt (dh von doppelten Anführungszeichen umgeben). Verwenden Sie Settype (arr ["var"], "integer") oder Settype ($ arr ["var"], "float"), um das Problem zu beheben.

<?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
quelle
2

Der Vollständigkeit halber (da ich noch keine Kommentare hinzufügen kann) möchte ich dieses Detail auch als weitere Antwort hinzufügen:

(Bearbeiten: Wird gelesen, nachdem erkannt wurde, dass die Quelldaten (dh im Fall des OP die Datenbank-Ergebnismenge) das Problem sein könnten (indem numerische Spalten als Zeichenfolgen zurückgegeben werden), und json_encode () war tatsächlich nicht die Quelle des Problems.)

Handbuchseiten beider " mysql_fetch_array ":

Gibt ein Array von Zeichenfolgen zurück , das der abgerufenen Zeile entspricht.

... und " mysql_ fetch_ row ":

Gibt ein numerisches Array von Zeichenfolgen zurück , das der abgerufenen Zeile entspricht

stellt klar fest, dass; Die Einträge im zurückgegebenen Array sind Zeichenfolgen.

(Ich habe die DB-Klasse in phpBB2 verwendet (ja, ich weiß, sie ist veraltet!), Und die Methode "sql_fetchrow ()" dieser Klasse verwendet "mysql_fetch_array ()".)

Da ich das nicht bemerkte, fand ich auch diese Frage und verstand das Problem! :) :)

Wie Pascal Martin oben in seinen nachfolgenden Kommentaren ausgeführt hat, glaube ich, dass eine Lösung das Problem des "falschen Typs" an der Quelle behebt ( dh indem die Funktion " mysql_field_type () " verwendet und das Casting direkt nach dem Abrufen durchgeführt wird (oder) andere Abrufmethoden wie "Objekt"?)) wären im Allgemeinen besser.

OzgurH
quelle
2

So Pascal MARTIN bekommt nicht genug Kredit hier. Die Überprüfung auf numerische Werte bei jeder JSON-Rückgabe ist für ein vorhandenes Projekt mit Hunderten von serverseitigen Funktionen nicht möglich.

Ich habe php-mysql durch php-mysqlnd ersetzt und das Problem ist verschwunden. Zahlen sind Zahlen, Zeichenfolgen sind Zeichenfolgen, Boolesche Werte sind Boolesche Werte.

Rory Jarrard
quelle
0

Ich hatte auch das gleiche Problem bei der Verarbeitung von Daten aus der Datenbank. Grundsätzlich besteht das Problem darin, dass der Typ in dem Array, der in json konvertiert werden soll, von PHP als Zeichenfolge und nicht als Ganzzahl erkannt wird. In meinem Fall habe ich eine Abfrage durchgeführt, die Daten aus einer DB-Spaltenzählzeile zurückgibt. Der PDO-Treiber erkennt die Spalte nicht als int, sondern als Zeichenfolgen. Ich habe es gelöst, indem ich eine Besetzung als int in der betroffenen Spalte ausgeführt habe.

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

Es ist PHP-Version das Problem, hatte das gleiche Problem meine PHP-Version auf 5.6 aktualisiert, löste das Problem

Peter Allen
quelle
0

Das Umwandeln der Werte in ein int oder float scheint dies zu beheben. Beispielsweise:

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

Sie können (int) verwenden, wenn ein Problem auftritt !! Es wird gut funktionieren.

Rahul Gupta
quelle
-1

Stoßen Sie einfach auf dasselbe Problem und die Datenbank hat die Werte als Zeichenfolgen zurückgegeben.

Ich benutze dies als Problemumgehung:

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

Das heißt, Sie multiplizieren den Wert mit 1, um ihn in eine Zahl umzuwandeln

Hoffe das hilft jemandem

Leon
quelle
Die Verwendung eines Multiplikators ist nicht die effizienteste Lösung. Erwägen Sie die Verwendung von JSON_NUMERIC_CHECK für json_encode, da dies automatisch behoben wird
Erick,
-2

json_encode serialisiert einige Datenstrukturen im JSON-Format, die über das Netzwerk gesendet werden sollen. Daher ist der gesamte Inhalt vom Typ Zeichenfolge. Genau wie wenn Sie einen Parameter von $ _POST oder $ _GET erhalten.

Wenn Sie numerische Operationen für die gesendeten Werte ausführen müssen, konvertieren Sie diese einfach zuerst in int (mit der Funktion intval () in PHP oder parseInt () in Javascript) und führen Sie dann die Operationen aus.

rogeriopvl
quelle
darüber redet er nicht. er spricht davon, dass der json doppelte Anführungszeichen um die Zahlen hat.
JasonWoof
Im Fall von JSON werden Typen beibehalten. Stellen Sie sich vor, Sie schreiben ein wörtliches JavaScript-Objekt, alle in Anführungszeichen gesetzten Werte werden zu Zeichenfolgen, aber nicht in Anführungszeichen gesetzte Zahlen werden zu Ganzzahlen, 0x [0-9a-z] wird zu Hex usw. Es gibt Typunterschiede mit PHP, z. B. gibt es keine ein assoziatives Array, nur Objekte oder indizierte Arrays usw.
Bucabay
richtig. Das Problem, das er hatte, war, dass er eine PHP-Variable hatte, von der er glaubte, dass sie Typ int hatte, weil sie aus einer DB-Spalte vom Typ int stammte. Tatsächlich hatte die PHP-Variable jedoch eine Typzeichenfolge, also die Anführungszeichen im JSON.
JasonWoof
-2

Nun, PHP json_encode () gibt einen String zurück.

Sie können parseFloat () oder parseInt () im js-Code verwenden:

parseFloat('122.5'); // returns 122.5
parseInt('22'); // returns 22
parseInt('22.5'); // returns 22
Richard Knop
quelle
Vielen Dank. Ich hatte gehofft, dass es eine elegantere Methode gibt.
Chris Barnhill
1
Ja, der sicherste Weg ist, sie zu analysieren. Aber ist Javascript nicht lose geschrieben?
Mauris
Nur ein Kommentar: Es ist lose geschrieben, dennoch wird das Ergebnis von "1" +1 ... 11 sein - also sollten Sie mit JS aufmerksamer sein als in starken Typensprachen, weil sie Sie warnen, JS tut einfach, was es ist denke, ist am besten, wenn man mit String-Nummern
umgeht
Ja, aber das ist nicht der Punkt. Json_encode sollte numerischen Feldern keine Anführungszeichen hinzufügen.
andreszs
-3

Wie oli_arborum sagte, ich denke, Sie können ein preg_replacefür die Arbeit verwenden. Ändern Sie einfach den Befehl wie folgt:

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