PHP json_encode codifica i numeri come stringhe


142

Sto riscontrando un problema con la funzione json_encode di PHP. Codifica i numeri come stringhe, ad es

array('id' => 3)

diventa

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

Quando js incontra questi valori, li interpreta come stringhe e operazioni numeriche falliscono su di essi. Qualcuno sa come impedire la json_encodecodifica dei numeri come stringhe? Grazie!


Si scopre che questo è un problema specifico della versione. A volte un pull da un database MySql manterrà i tipi corretti. Nelle versioni precedenti, potrebbe restituire tutto come una stringa. Ne ho scritto stamattina. shakyshane.com/blog/output-json-from-php.html
shane

1
Avevo lo stesso problema e sono stato in grado di risolvere il mio usando i mutatori di Laravel nel modello. Ti consente di modificare i valori nel modello. laravel.com/docs/eloquent#accessors-and-mutators All'inizio non l'ho capito bene, ma questa domanda mi ha aiutato: stackoverflow.com/questions/16985656/…
Jazzy,

Risposte:


28

Ho fatto un test molto veloce:

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

Questo sembra essere come quello che descrivi, se non sbaglio?

E sto ottenendo come output:

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

Quindi, in questo caso, gli interi non sono stati convertiti in stringa.


Tuttavia, ciò potrebbe dipendere dalla versione di PHP che stiamo usando: sono stati corretti un paio di bug relativi a json_encode, a seconda della versione di PHP ...

Questo test è stato effettuato con PHP 5.2.6; Sto ottenendo la stessa cosa con PHP 5.2.9 e 5.3.0; Non ho un'altra versione 5.2.x con cui testare, però :-(

Quale versione di PHP stai usando? O il tuo caso di prova è più complesso dell'esempio che hai pubblicato?

Forse una segnalazione di bug su http://bugs.php.net/ potrebbe essere correlata? Ad esempio, Bug # 40503: la conversione di numeri interi json_encode non è coerente con PHP ?


Forse il Bug # 38680 potrebbe interessarti anche tu, a proposito?


Grazie Martin. Sto usando 5.2.9. Mi chiedo se i dati numerici vengono letti dal db come stringa? Sono sicuro che i tipi di campo sono int, ma non riesco a pensare a un'altra spiegazione. Proverò il tuo test rapido sul mio sistema e vedrò se ottengo lo stesso risultato.
Chris Barnhill,

12
OK circa 5.2.9; se i tuoi dati provengono da un database, il problema potrebbe essere lì: ho visto spesso dati provenienti dal database con tutto il cast su stringa (l'ho visto con PDO e mssql; ma, se ricordo bene, questo succede anche per MySQL in PHP <5.3, quando il nuovo driver mysqlnd non era ancora esistente) ;; per verificare l'aspetto dei dati, è possibile utilizzare var_dump, che genera i tipi di ciascuna parte dei dati.
Pascal MARTIN,

(continua) pensa che il tipo di dati per i valori numerici sia stringa? Qualche idea?
Chris Barnhill,

1
Non conosco esattamente il motivo tecnico del "motivo per cui i dati vengono restituiti da MySQL come una stringa" ;; probabilmente qualcosa che ha a che fare con il driver tra PHP e MySQL ;; è qualcosa che è (almeno in alcuni casi) corretto dal nuovo driver mysqlnd fornito con PHP 5.3 (vedi blog.ulf-wendel.de/?p=184 ; cerca "numero intero" nella pagina per trovare l'interessante frase) ;; ma concordo sul fatto che non è bello ^^
Pascal MARTIN,

Non importa che i dati numerici restituiti da json_encode () non siano tra virgolette, è comunque una stringa. Il valore restituito della funzione json_encode () è una stringa. Per quanto riguarda MySQL che restituisce tutti i campi come stringhe, sì, l'ho riscontrato anche in modo specifico con PDO. Per come la vedo io dovresti sempre trasmettere valori che ti aspetti di essere numerici in numeri interi (o float) in PHP per essere sicuri, non fidarti di MySQL o di qualsiasi altro database per restituire valori con il tipo corretto.
Richard Knop,

352

Nota che da PHP 5.3.3 esiste un flag per la conversione automatica dei numeri (il parametro options è stato aggiunto in PHP 5.3.0):

$arr = array( 'row_id' => '1', 'name' => 'George' );
echo json_encode( $arr, JSON_NUMERIC_CHECK ); // {"row_id":1,"name":"George"}

4
Nota che JSON_NUMERIC_CHECK richiede PHP 5.3.3.
Robert,

10
Ha funzionato perfettamente fino a quando ha lanciato un'etichetta numerica su un numero intero, facendo esplodere .toLowerCase () in IE. Fai attenzione, questa soluzione è semplice ma troppo zelante.
Brad Koch,

6
SEI IL MIO EROE lo adoro.
Petrogad,

5
Ha qualche effetto collaterale se la tua stringa non è un numero ma contenuto come questo: 5252788e16597. riferimento: bugs.php.net/bug.php?id=64695
TonyQ

20
JSON_NUMERIC_CHECKtenta di indovinare automaticamente se una stringa è un numero o meno tentando di analizzarlo. È piuttosto inaffidabile se ci pensate. Converte tutte le proprietà dall'aspetto numerico in numeri (non solo quelli desiderati) e lo farà solo se sembrano numeri. Questo è almeno traballante se non pericoloso. Il codice che utilizza il JSON prodotto potrebbe fare affidamento sul fatto che il tipo sia l'uno o l'altro. Cose strane possono accadere se tali aspettative non vengono soddisfatte. Se ti interessano le buone pratiche e la sicurezza, dovresti convertire selettivamente i valori che desideri.
wadim,

35

Anch'io stavo leggendo da un DB (PostgreSQL) e tutto era una stringa. Eseguiamo il ciclo su ogni riga e facciamo le cose con esso per costruire il nostro array di risultati finali, quindi ho usato

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

all'interno del loop per forzarlo ad essere un valore intero. Quando lo faccio json_encode($result_arr)ora, lo formatta correttamente come un numero. Ciò consente di controllare ciò che è e non è un numero proveniente dal database.

MODIFICARE:

La json_encode()funzione ha anche la capacità di farlo al volo usando la JSON_NUMERIC_CHECKbandiera come secondo argomento. Devi stare attento ad usarlo, come mostrato nell'esempio di questo utente nella documentazione (copiato di seguito): http://uk3.php.net/manual/en/function.json-encode.php#106641

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

E poi ottieni questo JSON:

{"phone_number":33123456789}

sì, sembra che il problema sia dell'adattatore DB che non interpreta i tipi di dati, NON la json_encodefunzione. questa è la risposta più corretta, ma fai attenzione poiché JSON_NUMERIC_CHECKconverte anche i numeri di telefono e altri valori di stringa numerica, e questo potrebbe dare problemi nei primi zeri o '+' ... Suggerisco di correggere questo problema nella funzione di lettura del DB.
Caesarsol,


7

Sto riscontrando lo stesso problema (PHP-5.2.11 / Windows). Sto usando questa soluzione alternativa

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

che sostituisce tutti i numeri (non negativi, numeri interi) racchiusi tra virgolette con il numero stesso ('"42"' diventa '42').

Vedi anche questo commento nel manuale di PHP .


Grazie per il codice, ma purtroppo non ha funzionato sul mio JSON perché ho un numero come nome di un oggetto, e sembra rendere il JSON non valido :(
SSH Questo

@SSHQuesto, forse dovresti usare questa sintassi per convertire l'array in un array codificato JSON e non in un oggetto. $json_array = json_encode($some_array, false);Quindi il falso argomento dice a PHP di non fare la conversione dell'oggetto.
hyde,

Non è affatto sicuro usare questa soluzione alternativa. Otterrai un json non valido con strutture del genere:json_encode(array(-1=>'que', '0'=>'-1'))
Alex Yaroshevich,

Ho avuto un problema al contrario, avevo bisogno che i miei numeri interi fossero codificati come stringhe in PHP 7.0 e usato questo$this->data = preg_replace("/\" *?: *?(\d+)/", '":"$1"', $this->data);
Maciej Swic,

Cambierò la regex originale in `" /\"(\d+\.?\d*)\"/ "` per includere i decimali, un'altra nota è che i ragazzi che usano JSON_NUMERIC_CHECK dovranno affrontare il problema quando anche una stringa è corretta numero in notazione scientifica. ad es. 19E008. JSON_NUMERIC_CHECK lo convertirà in 190000 ...
Sahib Khan il

3

Il seguente test conferma che la modifica del tipo in stringa fa sì che json_encode () restituisca un valore numerico come stringa JSON (ovvero, racchiuso tra virgolette). Usa settype (arr ["var"], "integer") o settype ($ arr ["var"], "float") per risolverlo.

<?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>";

?>

2

Per completezza (poiché non posso ancora aggiungere commenti), vorrei aggiungere anche questo dettaglio come un'altra risposta:

(Modifica: da leggere dopo aver realizzato che i dati di origine (ovvero nel caso del PO, set di risultati del database) potrebbero essere il problema (restituendo colonne numeriche come stringhe), e json_encode () in realtà non era l'origine del problema)

Pagine del manuale di entrambi " mysql_fetch_array ":

Restituisce una matrice di stringhe che corrisponde alla riga recuperata,

... e " mysql_ fetch_ row ":

Restituisce una matrice numerica di stringhe che corrisponde alla riga recuperata

afferma chiaramente che; le voci nella matrice restituita saranno stringhe.

(Stavo usando la classe DB in phpBB2 (sì, lo so, è obsoleto!), E il metodo "sql_fetchrow ()" di quella classe usa "mysql_fetch_array ()")

Non rendendomi conto, ho anche finito per trovare questa domanda e capire il problema! :)

Come Pascal Martin ha affermato sopra nei suoi commenti di follow-up, credo che una soluzione che risolva il problema del "tipo errato" alla fonte (cioè usando la funzione " mysql_field_type () " e eseguendo il casting subito dopo il recupero, (o altri metodi di recupero come "oggetto"?)) sarebbero i migliori in generale.


2

Quindi Pascal MARTIN non sta ottenendo abbastanza credito qui. Il controllo di valori numerici su ogni ritorno JSON non è fattibile per un progetto esistente con centinaia di funzioni lato server.

Ho sostituito php-mysql con php-mysqlnd e il problema è scomparso. I numeri sono numeri, le stringhe sono stringhe, i booleani sono booleani.


0

Ho anche avuto lo stesso problema nell'elaborazione dei dati dal database. Fondamentalmente il problema è che il tipo nell'array da convertire in json, viene riconosciuto da PHP come una stringa e non come un numero intero. Nel mio caso ho creato una query che restituisce dati da una riga di conteggio delle colonne DB. Il driver PDO non riconosce la colonna come int, ma come stringhe. Ho risolto eseguendo un cast come int nella colonna interessata.


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

0

è la versione php il problema, lo stesso problema ha aggiornato la mia versione php a 5.6 risolto il problema


0

Il cast dei valori su un int o float sembra risolverlo. Per esempio:

$coordinates => array( 
    (float) $ap->latitude,
    (float) $ap->longitude 
);

0

Puoi usare (int) se si verificano problemi !! Funzionerà benissimo.


-1

Ho appena incontrato lo stesso problema e il database ha restituito i valori come stringhe.

Lo uso come soluzione alternativa:

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

Ciò sta moltiplicando il valore per 1 per convertirlo in un numero

Spero che aiuti qualcuno


l'utilizzo di un moltiplicatore non è la soluzione più efficiente. valuta la possibilità di utilizzare JSON_NUMERIC_CHECK su json_encode poiché lo risolverà automaticamente
Erick,

-2

json_encode serializza alcune strutture di dati in formato JSON da inviare attraverso la rete. Pertanto, tutto il contenuto sarà del tipo stringa. Proprio come quando ricevi alcuni parametri da $ _POST o $ _GET.

Se devi eseguire operazioni numeriche sui valori inviati, convertili prima in int (con la funzione intval () in PHP o parseInt () in Javascript) e quindi esegui le operazioni.


non è di questo che parla. sta parlando del json che ha doppie virgolette attorno ai numeri.
JasonWoof,

Nel caso di JSON, conserva i tipi. Immagina di scrivere un oggetto JavaScript letterale, tutti i valori tra virgolette diventano stringhe, ma i numeri non quotati diventano numeri interi, 0x [0-9a-z] diventa esadecimale, ecc. Ci sono differenze di tipo con PHP, come, non esiste nulla come un array associativo, solo oggetti o array indicizzati ecc.
bucabay

giusto. Il problema che stava avendo era che aveva una variabile php, che pensava avesse tipo int, perché proveniva da una colonna DB di tipo int. Ma in realtà la variabile PHP aveva tipo stringa, quindi le virgolette in JSON.
JasonWoof,

-2

Bene, PHP json_encode () restituisce una stringa.

Puoi usare parseFloat () o parseInt () nel codice js però:

parseFloat('122.5'); // returns 122.5
parseInt('22'); // returns 22
parseInt('22.5'); // returns 22

Grazie. Speravo ci fosse un metodo più elegante.
Chris Barnhill,

1
sì, il modo più sicuro è analizzarli. ma ancora una volta, Javascript non è tipicamente scritto?
mauris,

Solo un commento: è scritto in modo approssimativo, tuttavia il risultato di "1" +1 sarà ... 11 - quindi usando JS dovresti essere più attento che in linguaggi di tipo forte, perché ti avvertiranno, JS fa solo quello che è pensa che sia la cosa migliore se
gestisci

Sì, ma non è questo il punto, json_encode non dovrebbe aggiungere virgolette ai campi numerici.
Andreszs

-3

Come ha detto oli_arborum, penso che tu possa usare a preg_replaceper fare il lavoro. Basta cambiare il comando in questo modo:

$json = preg_replace('#:"(\d+)"#', ':$1', $json);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.