PHP json_encode codificando números como strings

142

Estou tendo um problema com a função json_encode do PHP. Codifica números como strings, por exemplo

array('id' => 3)

torna-se

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

Quando js encontra esses valores, ele os interpreta como seqüências de caracteres e operações numéricas falham neles. Alguém sabe alguma maneira de impedir a json_encodecodificação de números como seqüências de caracteres? Obrigado!

Chris Barnhill
fonte
Acontece que este é um problema específico da versão. Às vezes, um pull de um banco de dados MySql mantém os tipos corretos. Nas versões mais antigas, pode retornar tudo como uma string. Eu escrevi sobre isso esta manhã. shakyshane.com/blog/output-json-from-php.html
shane
1
Eu estava tendo o mesmo problema e consegui resolver o meu usando os Mutators do Laravel no modelo. Permite modificar os valores no modelo. laravel.com/docs/eloquent#accessors-and-mutators Eu não entendi direito no começo, mas essa pergunta ajudou: stackoverflow.com/questions/16985656/…
Jazzy

Respostas:

28

Eu fiz um teste muito rápido:

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

Parece ser o que você descreve, se não me engano?

E eu estou recebendo como saída:

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

Portanto, nesse caso, os números inteiros não foram convertidos em string.


Ainda assim, isso pode ser dependente da versão do PHP que estamos usando: houve alguns bugs relacionados ao json_encode corrigidos, dependendo da versão do PHP ...

Este teste foi feito com o PHP 5.2.6; Estou recebendo a mesma coisa com o PHP 5.2.9 e 5.3.0; Eu não tenho outra versão 5.2.x para testar, no entanto :-(

Qual versão do PHP você está usando? Ou seu caso de teste é mais complexo que o exemplo que você postou?

Talvez um relatório de erro em http://bugs.php.net/ possa estar relacionado? Por exemplo, Bug # 40503: a conversão de número inteiro json_encode é inconsistente com o PHP ?


Talvez o bug # 38680 também pudesse interessá-lo, btw?

Pascal MARTIN
fonte
Obrigado Martin. Estou usando o 5.2.9. Gostaria de saber se os dados numéricos estão sendo lidos do db como string? Tenho certeza de que os tipos de campo são int, mas não consigo pensar em outra explicação. Vou tentar o seu teste rápido no meu sistema e ver se obtive o mesmo resultado.
22620 Chris Barnhill
12
OK sobre 5.2.9; se seus dados vierem de um banco de dados, o problema pode estar lá: muitas vezes vi dados vindos do banco de dados com tudo convertido em string (vi isso com o DOP e o mssql; mas, se bem me lembro, isso também acontece para MySQL em PHP <5.3, quando o novo driver mysqlnd ainda não existia) ;; para verificar como são os seus dados, você pode usar var_dump, que gera os tipos de cada parte dos dados.
Pascal MARTIN
(continuação) acha que o tipo de dados para valores numéricos é string? Alguma ideia?
31720 Chris Barnhill
1
Não sei exatamente o motivo técnico de "por que os dados são retornados do MySQL como uma string" ;; provavelmente algo que tem a ver com o driver entre PHP e MySQL ;; isso é algo que é (pelo menos em alguns casos) corrigido pelo novo driver mysqlnd que acompanha o PHP 5.3 (veja blog.ulf-wendel.de/?p=184 ; procure por "número inteiro" na página para encontrar as informações interessantes. sentença) ;; mas concordo que isso não é bom ^^
Pascal MARTIN
Não importa que os dados numéricos retornados por json_encode () não estejam entre aspas, ainda é uma sequência. O valor de retorno da função json_encode () é uma sequência. Em relação ao MySQL retornando todos os campos como strings, sim, eu também encontrei isso especificamente com o DOP. Da maneira que eu vejo, você sempre deve converter valores que espera ser numéricos para números inteiros (ou flutuantes) no PHP para garantir que não confie no MySQL ou em qualquer outro banco de dados para retornar valores com o tipo correto.
Richard Knop
352

Note que desde o PHP 5.3.3, há uma flag para números de conversão automática (o parâmetro options foi adicionado no PHP 5.3.0):

$arr = array( 'row_id' => '1', 'name' => 'George' );
echo json_encode( $arr, JSON_NUMERIC_CHECK ); // {"row_id":1,"name":"George"}
Rijk
fonte
4
Observe que JSON_NUMERIC_CHECK requer o PHP 5.3.3.
Robert
10
Funcionou perfeito até converter um rótulo numérico em um número inteiro, explodindo .toLowerCase () no IE. Tenha cuidado, esta solução é simples, mas excessivamente zelosa.
21812 Brad
6
VOCÊ É MEU HERÓI Ame.
Petrogad 7/09/12
5
Tem algum efeito colateral se a sua sequência não for um número, mas um conteúdo como este: 5252788e16597. referência: bugs.php.net/bug.php?id=64695
TonyQ
20
JSON_NUMERIC_CHECKtenta adivinhar automaticamente se uma sequência é um número ou não, tentando analisá-la. Isso é muito pouco confiável se você pensar sobre isso. Ele converterá todas as propriedades de aparência numérica em números (não apenas aquelas que você deseja) e fará isso apenas se parecerem com números. Isso é pelo menos instável, se não inseguro. O código que consome o JSON produzido pode depender do tipo que é um ou outro. Coisas estranhas podem acontecer se essas expectativas não forem atendidas. Se você se preocupa com boas práticas e segurança, deve converter seletivamente os valores que deseja.
Wadim
35

Eu também estava lendo um banco de dados (PostgreSQL) e tudo era uma string. Passamos por cada linha e fazemos as coisas para criar nossa matriz de resultados finais, então eu usei

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

dentro do loop para forçá-lo a ser um valor inteiro. Quando o faço json_encode($result_arr)agora, ele o formata corretamente como um número. Isso permite que você controle o que é e o que não é um número proveniente do seu banco de dados.

EDITAR:

A json_encode()função também tem a capacidade de fazer isso rapidamente, usando a JSON_NUMERIC_CHECKbandeira como um segundo argumento para ela. Você precisa ter cuidado ao usá-lo, conforme mostrado neste exemplo de usuário na documentação (copiada abaixo): 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 então você obtém esse JSON:

{"phone_number":33123456789}
mouckatron
fonte
Sim, parece que o problema é do adaptador DB que não interpreta os tipos de dados, NÃO da json_encodefunção. esta é a resposta mais correta, mas tenha cuidado, pois JSON_NUMERIC_CHECKconverte também números de telefone e outros valores de sequência numérica, e isso pode causar problemas nos zeros à esquerda ou '+' ... Sugiro corrigir esse problema na função de leitura do banco de dados.
caesarsol
8

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

Mas apenas funciona no PHP 5.3.3. Veja este log de alterações do PHP json_encode http://php.net/manual/en/function.json-encode.php#refsect1-function.json-encode-changelog

habibillah
fonte
Isso funcionou para mim. Instalação padrão do PHP + Apache no debian aperto 6.0.5 com dados provenientes de db postgre
Nikolay Spassov
7

Estou com o mesmo problema (PHP-5.2.11 / Windows). Estou usando esta solução alternativa

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

que substitui todos os números (não negativos, inteiros) entre aspas pelo próprio número ('"42"' se torna '42').

Veja também este comentário no manual do PHP .

oli_arborum
fonte
Agradecimentos para o código, mas, infelizmente, não funcionou no meu JSON porque eu tenho um número como um nome de objeto, e parece tornar o JSON inválida :(
SSH Este
@ SSHThis, talvez você deva usar essa sintaxe para converter a matriz em uma matriz codificada em JSON e não em um objeto. $json_array = json_encode($some_array, false);Portanto, o argumento false diz ao PHP para não fazer a conversão do objeto.
Hyde
Não é seguro usar essa solução alternativa. Você receberá json inválido com estruturas como essa:json_encode(array(-1=>'que', '0'=>'-1'))
Alex Yaroshevich
Eu tive um problema ao contrário, eu precisava dos meus números inteiros codificados como seqüências de caracteres no PHP 7.0 e usei isso$this->data = preg_replace("/\" *?: *?(\d+)/", '":"$1"', $this->data);
#
Vou alterar o regex original para `" /\"(\d+\.?\d*)\"/ "` para incluir decimais, outra observação é que os caras que usam JSON_NUMERIC_CHECK enfrentarão o problema quando uma string também estiver correta número em notação científica. por exemplo, 19E008. JSON_NUMERIC_CHECK irá convertê-lo para 190000 ...
Sahib Khan
3

O teste a seguir confirma que alterar o tipo para string faz com que json_encode () retorne um numérico como uma string JSON (ou seja, entre aspas duplas). Use settype (arr ["var"], "número inteiro") ou settype ($ arr ["var"], "float") para corrigi-lo.

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

Por uma questão de integridade (como ainda não posso adicionar comentários), deixe-me também adicionar esse detalhe como outra resposta:

(Editar: para ser lido depois de perceber que os dados de origem (por exemplo, no caso do OP, conjunto de resultados do banco de dados) podem ser o problema (retornando colunas numéricas como seqüências de caracteres) e json_encode () na verdade não era a origem do problema)

Páginas de manual de ambos " mysql_fetch_array ":

Retorna uma matriz de strings que corresponde à linha buscada,

... e " mysql_ fetch_ row ":

Retorna uma matriz numérica de seqüências de caracteres que corresponde à linha buscada

afirma claramente isso; as entradas na matriz retornada serão cadeias de caracteres.

(Eu estava usando a classe DB no phpBB2 (sim, eu sei, é obsoleto!), E o método "sql_fetchrow ()" dessa classe usa "mysql_fetch_array ()")

Não percebendo isso, acabei encontrando essa pergunta e entendendo o problema! :)

Como Pascal Martin afirmou acima em seus comentários de acompanhamento, acredito que uma solução que cuida do problema do "tipo incorreto" na fonte (ou seja, usando a função " mysql_field_type () " e fazendo a conversão logo após a busca, (ou outros métodos de busca como "objeto"?)) seriam os melhores em geral.

OzgurH
fonte
2

Então Pascal MARTIN não está recebendo crédito suficiente aqui. A verificação de valores numéricos em cada retorno JSON não é viável para um projeto existente com centenas de funções do lado do servidor.

Substituí o php-mysql pelo php-mysqlnd e o problema desapareceu. Números são números, strings são strings, booleanos são booleanos.

Rory Jarrard
fonte
0

Eu também tive o mesmo problema ao processar dados do banco de dados. Basicamente, o problema é que o tipo na matriz a ser convertida em json é reconhecido pelo PHP como uma string e não como um número inteiro. No meu caso, fiz uma consulta que retorna dados de uma linha de contagem de colunas do banco de dados. O driver PDO não reconhece a coluna como int, mas como seqüências de caracteres. Resolvi realizando uma conversão como int na coluna afetada.

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

é a versão php o problema, tive o mesmo problema atualizei minha versão php para 5.6 resolvi o problema

Peter Allen
fonte
0

A conversão dos valores para um int ou float parece corrigi-lo. Por exemplo:

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

Você pode usar (int) se ocorrer algum problema! Vai funcionar bem.

Rahul Gupta
fonte
-1

Apenas execute o mesmo problema e o banco de dados retornará os valores como seqüências de caracteres.

Eu uso isso como uma solução alternativa:

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

Isso é multiplicar o valor por 1 para convertê-lo em um número

Espero que ajude alguém

Leon
fonte
usar um multiplicador não é a solução mais eficiente. considere o uso de JSON_NUMERIC_CHECK em json_encode, pois ele será corrigido automaticamente
Erick
-2

json_encode serializa algumas estruturas de dados no formato JSON para serem enviadas pela rede. Portanto, todo o conteúdo será do tipo string. Assim como quando você recebe algum parâmetro de $ _POST ou $ _GET.

Se você precisar fazer operações numéricas nos valores enviados, apenas converta-os para int primeiro (com a função intval () em PHP ou parseInt () em Javascript) e, em seguida, execute as operações.

rogeriopvl
fonte
não é disso que ele está falando. ele está falando sobre o json tendo aspas duplas em torno dos números.
JasonWoof
No caso de JSON, ele conserva tipos. Imagine escrever um objeto JavaScript literal, todos os valores entre aspas se tornarão strings, mas números não entre aspas se tornarão inteiros, 0x [0-9a-z] se tornará hexadecimal etc. Existem diferenças de tipo no PHP, como, por exemplo, não existe uma matriz associativa, apenas Objectos ou matrizes indexadas etc
bucabay
certo. O problema que ele estava tendo era que ele tinha uma variável php, que ele pensava ter do tipo int, porque vinha de uma coluna DB do tipo int. Mas, na verdade, a variável PHP tinha tipo string, portanto, as aspas no JSON.
JasonWoof
-2

Bem, o PHP json_encode () retorna uma string.

Você pode usar parseFloat () ou parseInt () no código js:

parseFloat('122.5'); // returns 122.5
parseInt('22'); // returns 22
parseInt('22.5'); // returns 22
Richard Knop
fonte
Obrigado. Eu esperava que houvesse um método mais elegante.
22620 Chris Barnhill
1
Sim, a maneira mais segura é analisá-los. mas, novamente, o Javascript não é digitado livremente?
mauris 08/09/09
Apenas um comentário: ele é digitado livremente, ainda o resultado de "1" +1 será ... 11 - portanto, usando JS, você deve ser mais atencioso do que em linguagens de tipos fortes, porque eles o alertam, JS faz apenas o que é necessário. acha que é melhor melhor ao manusear String-Números ...
SamiSalami
Sim, mas esse não é o ponto, json_encode não deve adicionar aspas aos campos numéricos.
andreszs
-3

Como oli_arborum disse, acho que você pode usar um preg_replacepara fazer o trabalho. Apenas mude o comando assim:

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