PHP json_encode koduje liczby jako ciągi

142

Mam jeden problem z funkcją json_encode PHP. Koduje liczby jako ciągi znaków, np

array('id' => 3)

staje się

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

Gdy js napotyka te wartości, interpretuje je jako łańcuchy i operacje numeryczne kończą się niepowodzeniem. Czy ktoś zna sposób, aby zapobiec json_encodekodowaniu liczb jako ciągów znaków? Dziękuję Ci!

Chris Barnhill
źródło
Okazuje się, że jest to problem specyficzny dla wersji. Czasami pobieranie z bazy danych MySql zachowuje poprawne typy. W starszych wersjach może zwrócić wszystko jako ciąg. Pisałem o tym dziś rano. shakyshane.com/blog/output-json-from-php.html
shane
1
Miałem ten sam problem i udało mi się rozwiązać swój za pomocą mutatorów Laravela w modelu. Pozwala modyfikować wartości w modelu. laravel.com/docs/eloquent#accessors-and-mutators Na początku nie do końca to rozumiałem , ale to pytanie pomogło: stackoverflow.com/questions/16985656/…
Jazzy

Odpowiedzi:

28

Zrobiłem bardzo szybki test:

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

To wygląda na to, co opisujesz, jeśli się nie mylę?

I otrzymuję jako wyjście:

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

Zatem w tym przypadku liczby całkowite nie zostały przekonwertowane na łańcuch.


Jednak może to zależeć od używanej wersji PHP: poprawiono kilka błędów związanych z json_encode, w zależności od wersji PHP ...

Ten test został wykonany w PHP 5.2.6; Otrzymuję to samo z PHP 5.2.9 i 5.3.0; Nie mam jednak innej wersji 5.2.x do przetestowania :-(

Której wersji PHP używasz? A może Twój przypadek testowy jest bardziej złożony niż przykład, który opublikowałeś?

Może jeden raport o błędzie na http://bugs.php.net/ mógłby być powiązany? Na przykład błąd nr 40503: konwersja liczb całkowitych json_encode jest niezgodna z PHP ?


Może Bug # 38680 też może Cię zainteresować?

Pascal MARTIN
źródło
Dzięki Martin. Używam 5.2.9. Zastanawiam się, czy dane liczbowe są odczytywane z bazy danych jako ciąg? Jestem pewien, że typy pól to int, ale nie mogę wymyślić innego wyjaśnienia. Spróbuję wykonać szybki test na moim systemie i zobaczę, czy uzyskam ten sam wynik.
Chris Barnhill
12
OK około 5.2.9; jeśli twoje dane pochodzą z bazy danych, problem może tam być: często widziałem dane pochodzące z bazy danych, w której wszystko jest rzutowane na łańcuch (widziałem to z PDO i mssql; ale jeśli dobrze pamiętam, to również się dzieje dla MySQL w PHP <5.3, kiedy nowy sterownik mysqlnd jeszcze nie istniał) ;; aby sprawdzić, jak wyglądają twoje dane, możesz użyć var_dump, który wyświetla typy każdej części danych.
Pascal MARTIN
(ciąg dalszy) uważa, że ​​typ danych dla wartości liczbowych to łańcuch? Jakieś pomysły?
Chris Barnhill,
1
Nie znam dokładnie technicznego powodu "dlaczego dane są zwracane z MySQL jako ciąg znaków" ;; prawdopodobnie coś, co ma związek ze sterownikiem między PHP i MySQL ;; to jest coś, co jest (przynajmniej w niektórych przypadkach) poprawiane przez nowy sterownik mysqlnd dostarczany z PHP 5.3 (zobacz blog.ulf-wendel.de/?p=184 ; wyszukaj "integer" na stronie, aby znaleźć interesujące zdanie) ;; ale zgadzam się, że to nie jest miłe ^^
Pascal MARTIN
Nie ma znaczenia, że ​​dane liczbowe zwracane przez json_encode () nie są w cudzysłowach, nadal są to ciąg. Wartość zwracana przez funkcję json_encode () jest ciągiem znaków. Jeśli chodzi o MySQL zwracający wszystkie pola jako ciągi, tak, spotkałem się z tym również w przypadku PDO. Z mojego punktu widzenia, powinieneś zawsze rzutować wartości, które oczekujesz, że będą numeryczne na liczby całkowite (lub zmiennoprzecinkowe) w PHP, aby upewnić się, że nie ufaj MySQL ani żadnej innej bazie danych w celu zwrócenia wartości o poprawnym typie.
Richard Knop,
352

Zauważ, że od PHP 5.3.3 istnieje flaga do automatycznej konwersji liczb (parametr opcji został dodany w PHP 5.3.0):

$arr = array( 'row_id' => '1', 'name' => 'George' );
echo json_encode( $arr, JSON_NUMERIC_CHECK ); // {"row_id":1,"name":"George"}
Rijk
źródło
4
Zauważ, że JSON_NUMERIC_CHECK wymaga PHP 5.3.3.
Robert
10
Działało idealnie, dopóki nie rzuciło etykiety numerycznej na liczbę całkowitą, wysadzając .toLowerCase () w IE. Uważaj, to rozwiązanie jest proste, ale nadgorliwe.
Brad Koch,
6
JESTEŚ MÓJ BOHATEREM, uwielbiam to.
Petrogad,
5
Ma to jakiś efekt uboczny, jeśli twój ciąg nie jest liczbą, ale zawartością taką: 5252788e16597. odniesienie: bugs.php.net/bug.php?id=64695
TonyQ
20
JSON_NUMERIC_CHECKpróbuje automatycznie zgadnąć, czy ciąg jest liczbą, czy nie, próbując go przeanalizować. Jeśli się nad tym zastanowić, jest to dość niewiarygodne. Przekształci wszystkie właściwości wyglądające liczbowo na liczby (nie tylko te, które chcesz) i zrobi to tylko wtedy, gdy będą wyglądać jak liczby. To przynajmniej chwiejne, jeśli nie niebezpieczne. Kod, który używa utworzonego kodu JSON, może polegać na typie będącym jednym lub drugim. Dziwne rzeczy mogą się wydarzyć, jeśli te oczekiwania nie zostaną spełnione. Jeśli zależy Ci na dobrych praktykach i bezpieczeństwie, powinieneś selektywnie konwertować wartości, które chcesz.
wadim,
35

Ja również czytałem z bazy danych (PostgreSQL) i wszystko było ciągiem znaków. Zapętlamy każdy wiersz i robimy z nim różne rzeczy, aby zbudować naszą ostateczną tablicę wyników, więc użyłem

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

w pętli, aby wymusić na niej wartość całkowitą. Kiedy json_encode($result_arr)teraz to zrobię , poprawnie formatuje go jako liczbę. Pozwala to kontrolować, co jest, a co nie jest numerem pochodzącym z Twojej bazy danych.

EDYTOWAĆ:

json_encode()Funkcja ma również możliwość, aby to zrobić w locie przy użyciu JSON_NUMERIC_CHECKflagi jako drugi argument do niego. Musisz jednak uważać, używając go, jak pokazano na tym przykładzie użytkownika w dokumentacji (skopiowanej poniżej): http://uk3.php.net/manual/en/function.json-encode.php#106641

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

A potem otrzymujesz ten JSON:

{"phone_number":33123456789}
mouckatron
źródło
tak, wygląda na to, że problem dotyczy adaptera DB, który nie interpretuje typów danych, a NIE json_encodefunkcji. jest to najbardziej poprawna odpowiedź, ale bądź ostrożny, ponieważ JSON_NUMERIC_CHECKkonwertuje również numery telefonów i inne wartości liczbowe ciągów, co może powodować problemy z początkowymi zerami lub „+” ... Proponuję rozwiązać ten problem w funkcji odczytu bazy danych.
caesarsol
8

próbować $arr = array('var1' => 100, 'var2' => 200);
$json = json_encode( $arr, JSON_NUMERIC_CHECK);

Ale działa tylko na PHP 5.3.3. Spójrz na ten dziennik zmian json_encode PHP http://php.net/manual/en/function.json-encode.php#refsect1-function.json-encode-changelog

habibillah
źródło
To zadziałało dla mnie. Domyślna instalacja PHP + apache na Debian squeeze 6.0.5 z danymi pochodzącymi z bazy danych postgre
Nikolay Spassov
7

Mam ten sam problem (PHP-5.2.11 / Windows). Używam tego obejścia

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

która zastępuje wszystkie (nieujemne, całkowite) liczby ujęte w cudzysłowy samą liczbą („42” staje się „42”).

Zobacz także ten komentarz w podręczniku PHP .

oli_arborum
źródło
Dzięki za kod, ale niestety nie zadziałał na moim jsonie, ponieważ mam numer jako nazwę obiektu i wydaje się, że ten kod json jest nieprawidłowy :(
SSH
@SSH To może powinieneś użyć tej składni do konwersji tablicy na tablicę zakodowaną w formacie JSON, a nie obiekt. $json_array = json_encode($some_array, false);Zatem fałszywy argument mówi PHP, aby nie wykonywał konwersji obiektów.
hyde
Korzystanie z tego obejścia nie jest wcale bezpieczne. Otrzymasz nieprawidłowy json_encode(array(-1=>'que', '0'=>'-1'))
plik JSON
Miałem problem na odwrót, potrzebowałem moich liczb całkowitych zakodowanych jako ciągi znaków w PHP 7.0 i użyłem tego$this->data = preg_replace("/\" *?: *?(\d+)/", '":"$1"', $this->data);
Maciej Swic
Zmienię oryginalne wyrażenie regularne na `" /\"(\d+\.?\d*)\"/ ", aby uwzględnić ułamki dziesiętne, kolejna uwaga jest taka, że ​​faceci używający JSON_NUMERIC_CHECK napotkają problem, gdy ciąg również będzie poprawny numer w notacji naukowej. np. 19E008. JSON_NUMERIC_CHECK przekonwertuje go na 190000 ...
Sahib Khan,
3

Poniższy test potwierdza, że ​​zmiana typu na ciąg powoduje, że json_encode () zwraca wartość liczbową jako ciąg JSON (tj. Ujęty w podwójne cudzysłowy). Użyj settype (arr ["var"], "integer") lub settype ($ arr ["var"], "float"), aby to naprawić.

<?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
źródło
2

Ze względu na kompletność (ponieważ nie mogę jeszcze dodawać komentarzy), dodam również ten szczegół jako kolejną odpowiedź:

Edytuj

Strony podręcznika dla obu „ mysql_fetch_array ”:

Zwraca tablicę ciągów odpowiadającą pobranemu wierszowi,

... i „ mysql_ fetch_ row ”:

Zwraca numeryczną tablicę ciągów odpowiadającą pobranemu wierszowi

jasno to stwierdza; wpisy w zwróconej tablicy będą ciągami.

(Używałem klasy DB w phpBB2 (tak wiem, jest przestarzała!), A metoda „sql_fetchrow ()” tej klasy używa „mysql_fetch_array ()”)

Nie zdając sobie z tego sprawy, w końcu znalazłem to pytanie i zrozumiałem problem! :)

Jak Pascal Martin stwierdził powyżej w swoich komentarzach uzupełniających, uważam, że rozwiązanie, które rozwiązuje problem „nieprawidłowego typu” u źródła (tj. Używając funkcji „ mysql_field_type () ” i wykonując rzutowanie zaraz po pobraniu (lub inne metody pobierania, takie jak „obiekt”?)) byłyby ogólnie lepsze.

OzgurH
źródło
2

Więc Pascal MARTIN nie ma tutaj wystarczających kredytów. Sprawdzanie wartości liczbowych w każdym zwrocie JSON jest niewykonalne w przypadku istniejącego projektu z setkami funkcji po stronie serwera.

Zastąpiłem php-mysql php-mysqlnd i problem zniknął. Liczby to liczby, łańcuchy to łańcuchy, wartości logiczne to wartości logiczne.

Rory Jarrard
źródło
0

Miałem też ten sam problem z przetwarzaniem danych z bazy danych. Zasadniczo problem polega na tym, że typ w tablicy do konwersji w json jest rozpoznawany przez PHP jako ciąg znaków, a nie jako liczba całkowita. W moim przypadku wykonałem zapytanie, które zwraca dane z wiersza liczącego kolumny DB. Sterownik PDO nie rozpoznaje kolumny jako int, ale jako ciągi. Rozwiązałem, wykonując rzutowanie jako int w odpowiedniej kolumnie.

balucio
źródło
0
$rows = array();
while($r = mysql_fetch_assoc($result)) {
    $r["id"] = intval($r["id"]); 
    $rows[] = $r;
}
print json_encode($rows);  
Yar
źródło
0

to jest wersja php problem, ten sam problem, który zaktualizował moją wersję php do 5.6, rozwiązał problem

Peter Allen
źródło
0

Rzutowanie wartości na int lub float wydaje się to naprawiać. Na przykład:

$coordinates => array( 
    (float) $ap->latitude,
    (float) $ap->longitude 
);
Derrick Miller
źródło
0

Możesz użyć (int), jeśli wystąpi jakikolwiek problem !! Będzie działać dobrze.

Rahul Gupta
źródło
-1

Po prostu napotkałem ten sam problem i baza danych zwracała wartości jako ciągi.

Używam tego jako obejścia:

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

Oznacza to pomnożenie wartości przez 1, aby rzucić ją na liczbę

Mam nadzieję, że to komuś pomoże

Leon
źródło
użycie mnożnika nie jest najbardziej wydajnym rozwiązaniem. rozważ użycie JSON_NUMERIC_CHECK na json_encode, ponieważ automatycznie to naprawi
Erick,
-2

json_encode serializuje niektóre struktury danych w formacie JSON do wysłania przez sieć. Dlatego cała zawartość będzie typu string. Tak jak w przypadku otrzymania jakiegoś parametru z $ _POST lub $ _GET.

Jeśli musisz wykonać operacje numeryczne na wysłanych wartościach, po prostu najpierw przekonwertuj je na int (za pomocą funkcji intval () w PHP lub parseInt () w JavaScript), a następnie wykonaj te operacje.

rogeriopvl
źródło
nie o tym on mówi. on mówi o jsonie mającym podwójne cudzysłowy wokół liczb.
JasonWoof,
W przypadku JSON zachowuje typy. Wyobraź sobie, że wypisujesz dosłowny obiekt JavaScript, wszystkie cytowane wartości stają się ciągami znaków, ale nie-cudzysłowowe liczby stają się liczbami całkowitymi, 0x [0-9a-z] staje się szesnastkowym itp. Istnieją różnice między typami w PHP, takie jak nie ma czegoś takiego jak tablica asocjacyjna, po prostu Obiekty lub tablice indeksowane itp.
bucabay
dobrze. Problem, jaki miał, polegał na tym, że miał zmienną php, która, jak sądził, miała typ int, ponieważ pochodziła z kolumny DB typu int. Ale w rzeczywistości zmienna PHP miała typ string, stąd cudzysłowy w JSON.
JasonWoof,
-2

Cóż, PHP json_encode () zwraca ciąg.

Możesz jednak użyć parseFloat () lub parseInt () w kodzie js:

parseFloat('122.5'); // returns 122.5
parseInt('22'); // returns 22
parseInt('22.5'); // returns 22
Richard Knop
źródło
Dzięki. Miałem nadzieję, że istnieje bardziej elegancka metoda.
Chris Barnhill
1
tak, najbezpieczniejszym sposobem jest ich przeanalizowanie. ale znowu, czy Javascript nie jest luźno wpisany?
Mauris
Tylko komentarz: Jest luźno wpisany, mimo to wynik "1" +1 będzie ... 11 - więc używając JS powinieneś być bardziej uważny niż w silnych językach pisanych, ponieważ będą cię ostrzegać, JS po prostu robi to, co robi myślę, że najlepiej będzie obsługiwać String-Numbers ...
SamiSalami
Tak, ale nie o to chodzi, json_encode nie powinien dodawać cudzysłowów do pól numerycznych.
andreszs
-3

Jak powiedział oli_arborum, myślę, że możesz użyć preg_replacedo wykonania tej pracy. Po prostu zmień polecenie w ten sposób:

$json = preg_replace('#:"(\d+)"#', ':$1', $json);
Santini Arnaud
źródło