165

I am having one problem with the PHP json_encode function. It encodes numbers as strings, e.g.

array('id' => 3)

becomes

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

When js encounters these values, it interprets them as strings and numeric operations fail on them. Does anyone know some way to prevent json_encode from encoding numbers as strings? Thank you!

Gottlieb Notschnabel
  • 9,408
  • 18
  • 74
  • 116
Chris Barnhill
  • 1,957
  • 3
  • 16
  • 18
  • 1
    I was having the same issue and I was able to solve mine using Laravel's Mutators in the Model. It lets you modify the values in the model. http://laravel.com/docs/eloquent#accessors-and-mutators I didn't quite get it at first, but this question helped: http://stackoverflow.com/questions/16985656/laravel-problems-with-mutators – Jazzy Sep 03 '14 at 04:24
  • It turns out that this is a version-specific problem. Sometimes a pull from a MySql database will maintain the correct types. In older versions, it may return everything as a string. I wrote about it this morning. http://shakyshane.com/blog/output-json-from-php.html – shane Jan 10 '13 at 11:35

18 Answers18

395

Note that since PHP 5.3.3, there's a flag for auto-converting numbers (the options parameter was added in PHP 5.3.0):

$arr = array( 'row_id' => '1', 'name' => 'George' );
echo json_encode( $arr, JSON_NUMERIC_CHECK ); // {"row_id":1,"name":"George"}
Rijk
  • 11,032
  • 3
  • 30
  • 45
  • 5
    Note that JSON_NUMERIC_CHECK requires PHP 5.3.3. – Robert Jan 13 '12 at 14:51
  • 11
    Worked perfect until it cast a numeric label to an integer, blowing up .toLowerCase() in IE. Be careful, this solution is simple but overzealous. – Brad Koch Jun 11 '12 at 21:57
  • @BradKoch Change the line to `.toString().toLowerCase()` that will work with either numbers or strings. – devios1 Oct 01 '12 at 20:31
  • 5
    It has some side effect if your string is not a number but content like this: 5252788e16597 . reference: https://bugs.php.net/bug.php?id=64695 – TonyQ Oct 07 '13 at 09:14
  • 25
    `JSON_NUMERIC_CHECK` attempts to automatically guess if a string is a number or not by trying to parse it. That is pretty unreliable if you think about it. It will convert **all** numeric-looking properties to numbers (not only those you want) and it will do that only if they look like numbers. That's at least shaky if not unsafe. The code that consumes the produced JSON might rely on the type being one or the other. Strange things can happen if those expectations are not met. If you care about good practices and safety you should selectively convert the values you want. – wadim Aug 11 '14 at 14:50
  • 2
    This is dangerous! Strings like "3424e123" and "+18675309" will be written to JSON as numbers. – NateS Jul 27 '15 at 12:55
  • 5
    As was mentioned by Brad and wadim, JSON_NUMERIC_CHECK has a side effect that converts legit strings to numeric values. To overcome that in case you know the type of each field, you can use the following workaround: concatenate to the value of any string field a 'marker' (e.g. 'this_is_a_string'), then performe the json_encode with the JSON_NUMERIC_CHECK option, and finally removed the marker from the json string: $cleaned_json= str_replace('this_is_a_string', '', $marked_json). Not elegant, but worked for me. – Doigen Jan 05 '16 at 12:29
  • why this overhead? while database query is returning integer. – sifr_dot_in Jan 13 '21 at 06:14
  • JSON_NUMERIC_CHECK changed all numer looking fields to numbers. I only wanted actual numeric quantities to be treated as numbers, so chose to add 0 to that field alone as suggested in https://stackoverflow.com/questions/8529656/how-do-i-convert-a-string-to-a-number-in-php – Polymath Aug 24 '22 at 10:31
36

I, likewise was reading from a DB (PostgreSQL) and everything was a string. We loop over each row and do things with it to build up our final results array, so I used

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

within the loop to force it to be an integer value. When I do json_encode($result_arr) now, it correctly formats it as a number. This allows you to control what is and is not a number coming from your database.

EDIT:

The json_encode() function also has the ability to do this on the fly using the JSON_NUMERIC_CHECK flag as a second argument to it. You need to be careful using it though as shown in this users example in the documentation (copied below): http://uk3.php.net/manual/en/function.json-encode.php#106641

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

And then you get this JSON:

{"phone_number":33123456789}
mouckatron
  • 1,289
  • 2
  • 13
  • 23
  • yes, it appears the problem is of the DB adapter which doesn't interpret the data types, NOT of the `json_encode` function. this is the most correct answer, but be careful since `JSON_NUMERIC_CHECK` converts also phone numbers and other numeric string values, and this could give problems in leading zeros or '+'... **I suggest correcting this problem in the DB reading function.** – caesarsol Nov 26 '14 at 11:37
32

I've done a very quick test :

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

This seems to be like what you describe, if I'm not mistaken ?

And I'm getting as output :

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

So, in this case, the integers have not been converted to string.


Still, this might be dependant of the version of PHP we are using : there have been a couple of json_encode related bugs corrected, depending on the version of PHP...

This test has been made with PHP 5.2.6 ; I'm getting the same thing with PHP 5.2.9 and 5.3.0 ; I don't have another 5.2.x version to test with, though :-(

Which version of PHP are you using ? Or is your test-case more complex than the example you posted ?

Maybe one bug report on http://bugs.php.net/ could be related ? For instance, Bug #40503 : json_encode integer conversion is inconsistent with PHP ?


Maybe Bug #38680 could interest you too, btw ?

Pascal MARTIN
  • 395,085
  • 80
  • 655
  • 663
  • Thanks Martin. I'm using 5.2.9. I wonder if the numeric data's getting read from the db as string? I'm sure the field types are int, but I can't think of another explanation. I'll try your quick test on my system and see if I get the same result. – Chris Barnhill Sep 07 '09 at 22:07
  • 14
    OK about 5.2.9 ; if your data is coming from a database, the problem might be there : I've often seen data come from database with everything casted to string (I've seen this with PDO and mssql ; but, if I remember correctly, this also happens for MySQL in PHP < 5.3, when the new mysqlnd driver was not yet existing) ;; to check what your data look like, you can use var_dump, which outputs the types of each part of the data. – Pascal MARTIN Sep 07 '09 at 22:14
  • (continued) thinks the data type for numeric values is string? Any ideas? – Chris Barnhill Sep 07 '09 at 22:15
  • 1
    I don't exactly know the technical reason of "why the data is returned from MySQL as a string" ;; probably something that has to do with the driver between PHP and MySQL ;; that is something that is (at least in some case) correcte by the new mysqlnd driver that comes with PHP 5.3 (see http://blog.ulf-wendel.de/?p=184 ; search for "integer" in the page to find the interesting sentence) ;; but I agree that this is not nice ^^ – Pascal MARTIN Sep 07 '09 at 22:26
  • It doesn't matter that numeric data returned by json_encode() is not in quotes, it is still a string. The return value of the json_encode() function is a string. Regarding the MySQL returning all fields as strings, yes I have also encountered this specifically with PDO. The way I see it you should always cast values you expect to be numeric to integer (or float) in PHP to make sure, do not trust MySQL or any other database to return values with correct type. – Richard Knop Sep 07 '09 at 23:49
  • Yeah might just be Database returning as string. I've encountered problems like this before and what I did simply was to do casting on PHP. – mauris Sep 08 '09 at 00:17
  • I think I found a way around this. I wrote this function for use with array_walk(). function numStrToInt(&$value, $key) { if(is_numeric($value) && is_string($value)) { $value = (int) $value; } } – Chris Barnhill Sep 08 '09 at 00:23
  • I intend to use this function on every result set array I get back from MySQL query. – Chris Barnhill Sep 08 '09 at 00:24
  • @Chris : I suppose converting numerics to strings is a possibility, if you really need integers to be... well... integers ^^ ;; another nice solution would be to use a layer that maps values to their expected types -- but that would require a bit more work ^^ – Pascal MARTIN Sep 08 '09 at 04:09
  • You could also loop through the results and cast it to integer (int) value. – Sebastian Sulinski May 08 '15 at 13:49
  • I had a similar issue but it had to do with the result set from a DB query. The issue was with the PHP MySQL adapter - mysqlnd. The selected answer in this post provides more information http://stackoverflow.com/questions/1197005/how-to-get-numeric-types-from-mysql-using-pdo – sharmil Jul 30 '15 at 08:11
  • 3
    I'm using 7 still have this problem – Steve Moretz Jun 11 '19 at 13:22
9

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

But it just work on PHP 5.3.3. Look at this PHP json_encode change log http://php.net/manual/en/function.json-encode.php#refsect1-function.json-encode-changelog

habibillah
  • 111
  • 1
  • 1
7

I'm encountering the same problem (PHP-5.2.11/Windows). I'm using this workaround

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

which replaces all (non-negative, integer) numbers enclosed in quotes with the number itself ('"42"' becomes '42').

See also this comment in PHP manual.

oli_arborum
  • 388
  • 3
  • 12
  • Thanks for the code, but sadly it didn't work on my json because I have a number as an object name, and it seems to render the json invalid :( – SSH This Apr 23 '13 at 00:25
  • @SSHThis, maybe you should use this syntax to convert the array into a JSON encoded array and not an object. `$json_array = json_encode($some_array, false);` So the false argument tells PHP to not do the object conversion. – hyde Jun 22 '13 at 22:33
  • It's not safe at all to use that workaround. You'll get invalid json with structures like that: `json_encode(array(-1=>'que', '0'=>'-1'))` – Alex Yaroshevich Oct 27 '13 at 23:39
  • I had a problem the other way around, i needed my integers encoded as strings in PHP 7.0 and used this `$this->data = preg_replace("/\" *?: *?(\d+)/", '":"$1"', $this->data);` – Maciej Swic Aug 21 '16 at 07:41
  • I will change the original regex to ` "/\"(\d+\.?\d*)\"/" ` to include decimals, another note is that guys who use JSON_NUMERIC_CHECK will face the problem when a string is also a correct number in scientific notation. e.g, 19E008. JSON_NUMERIC_CHECK will convert it to 190000... – Sahib Khan Dec 13 '19 at 03:02
3

The following test confirms that changing the type to string causes json_encode() to return a numeric as a JSON string (i.e., surrounded by double quotes). Use settype(arr["var"], "integer") or settype($arr["var"], "float") to fix it.

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

?>
3

So Pascal MARTIN isn't getting enough credit here. Checking for numeric values on every JSON return is not feasable for an existing project with hundreds of server side functions.

I replaced php-mysql with php-mysqlnd, and the problem went away. Numbers are numbers, strings are strings, booleans are boolean.

Rory Jarrard
  • 361
  • 2
  • 2
2

For sake of completeness (as I can't add comments yet), let me also add this detail as another answer:

(Edit: To be read after realizing that the source data (i.e. in the OP's case, database result set) could be the problem (by returning numeric columns as strings), and json_encode() in fact was not the source of the problem)

Manual pages of both "mysql_fetch_array":

Returns an array of strings that corresponds to the fetched row,

... and "mysql_ fetch_ row":

Returns an numerical array of strings that corresponds to the fetched row

clearly states that; the entries in the returned array will be strings.

(I was using the DB class in phpBB2 (yes I know, it's obsolete!), and "sql_fetchrow()" method of that class uses "mysql_fetch_array()")

Not realizing that, I also ended up finding this question, and understanding the problem! :)

As Pascal Martin stated above in his follow-up comments, I believe a solution that takes care of the "incorrect type" problem at the source (i.e. by using the "mysql_field_type()" function and doing the casting right after fetch, (or other fetch methods like "object"?)) would be the better in general.

OzgurH
  • 443
  • 2
  • 13
1

Casting the values to an int or float seems to fix it. For example:

$coordinates => array( 
    (float) $ap->latitude,
    (float) $ap->longitude 
);
Derrick Miller
  • 1,860
  • 3
  • 21
  • 37
0
$rows = array();
while($r = mysql_fetch_assoc($result)) {
    $r["id"] = intval($r["id"]); 
    $rows[] = $r;
}
print json_encode($rows);  
Yar
  • 4,543
  • 2
  • 35
  • 42
0

it is php version the problem, had the same issue upgraded my php version to 5.6 solved the problem

0

You can use (int) if any issue occurs!! It will work fine.

0

I also had the same problem processing data from the database. Basically the problem is that the type in the array to convert in json, is recognized by PHP as a string and not as integer. In my case I made a query that returns data from a DB column counting row. The PDO driver does not recognize the column as int, but as strings. I solved by performing a cast as int in the affected column.

balucio
  • 124
  • 3
  • 6
0

I have this problem in a curl sending json that required some fields to be integer and other fields string, but some of the values of the string fields were in fact numbers so JSON_NUMERIC_CHECK didn't worked because it converted everything that looked as a number to number. The solution I've found was to add characters representing future double quotes, in my case I've used @ as I knew it was impossible to have this character in my data, to the fields representing string values

$analysis = array(

'SampleAnalysisId'  => $record[3],
'DisplayValue' => '@'.$record[4].'@',
'MeasurementUnit' => '@'.$record[5].'@',
'SampleAnalysisConclusionId'  => $record[6],
'Uncertaint' => '@'.$record[7].'@',
'K' => '',
'QuantificationLimit' => '@'.$record[8].'@',
'DetectionLimit' => '@'.$record[9].'@',
'Veff' => ''

);

Then after encoding I replaced the double quotes for empty and then replaced the '@' for double quotes.

$str = json_encode($analysis,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
$str = str_replace('@','"',str_replace('",',',',str_replace(':"',':',$str)));

Resulting in a formatted json according to my need

[{"SampleAnalysisId":10479,"DisplayValue":"6,3","MeasurementUnit":"mg/L","SampleAnalysisConclusionId":1,"Uncertaint":"0,194463","K":,"QuantificationLimit":"1","DetectionLimit":"0,30","Veff":"}]
lisandro
  • 454
  • 4
  • 12
-1

Just run into the same problem and was the database returning the values as strings.

I use this as a workaround:

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

That is multiplying the value by 1 to cast it into a number

Hope that helps someone

Leon
  • 1,793
  • 2
  • 18
  • 20
  • using a multiplier is not the most efficient solution. consider using JSON_NUMERIC_CHECK on json_encode since it will automatically fix it – Erick Dec 08 '16 at 16:54
-2

json_encode serializes some data structure in JSON format to be send across the network. Therefore all content will be of the type string. Just like when you receive some parameter from $_POST or $_GET.

If you have to make numeric operations on the values sent, just convert them to int first (with the intval() function in PHP or parseInt() in Javascript) and then execute the operations.

rogeriopvl
  • 51,659
  • 8
  • 55
  • 58
  • that's not what he's talking about. he's talking about the json having double quotes around the numbers. – JasonWoof Sep 08 '09 at 00:10
  • In the case of JSON, it does conserve Types. Imagine writing out a literal JavaScript Object, any quoted values become strings, but non-quoted numbers become integers, 0x[0-9a-z] becomes hex etc. There are type differences with PHP, such as, there is no such thing as an associative array, just Objects or indexed arrays etc. – bucabay Sep 08 '09 at 00:25
  • right. The problem he was having, was that he had a php variable, that he thought had type int, because it came from a DB column of type int. But in fact the PHP variable had type string, thus the quotes in the JSON. – JasonWoof Sep 08 '09 at 03:10
-2

Well, PHP json_encode() returns a string.

You can use parseFloat() or parseInt() in the js code though:

parseFloat('122.5'); // returns 122.5
parseInt('22'); // returns 22
parseInt('22.5'); // returns 22
Richard Knop
  • 81,041
  • 149
  • 392
  • 552
  • Thanks. I was hoping there was a more elegant method. – Chris Barnhill Sep 07 '09 at 21:35
  • 1
    yeah, the safest way is to parse over them. but again, isn't Javascript loosely typed? – mauris Sep 08 '09 at 00:16
  • Just a comment: It is loosely typed, still the outcome of "1"+1 will be... 11 - so using JS you should be more attentious than in strong type languages, because they will warn you, JS just does what it's think is best best if handling String-Numbers... – SamiSalami Sep 02 '12 at 19:59
  • Yes but that's not the point, json_encode should not add quotes to numeric fields. – andreszs Jan 09 '15 at 02:57
-3

Like oli_arborum said, I think you can use a preg_replace for doing the job. Just change the command like this :

$json = preg_replace('#:"(\d+)"#', ':$1', $json);
Achrome
  • 7,773
  • 14
  • 36
  • 45