20

I'm using FQL to retrieve a list of users from Facebook. For consistency I get the result as JSON. This causes a problem - since the returned JSON encodes the user IDs as numbers, json_decode() converts these numbers to floating point values, because some are too big to fit in an int; of course, I need these IDs as strings.

Since json_decode() does its own thing without accepting any behavior flags, I'm at a loss. Any suggestions on how to resolve this?

ggambetta
  • 3,312
  • 6
  • 32
  • 42

7 Answers7

28

json_decode() can convert large integers to strings, if you specify a flag in the function call:

$array = json_decode($json, true, 512, JSON_BIGINT_AS_STRING)
Björn
  • 29,019
  • 9
  • 65
  • 81
  • No idea how could I miss that in the documentation. Thanks! – ggambetta May 28 '10 at 20:59
  • This only is in a dev version of PHP - at least according to the documentation on php.net? – Sebastian Hoitz Nov 15 '10 at 09:27
  • @Sebastian Hoitz , The page has not been updated in a long time, according to it the parameter should be available from PHP 5.3 (which was released a "long" time ago). – Björn Nov 15 '10 at 09:43
  • I just tried it on my local computer and got an error that json_decode only accepts 3 parameters. – Sebastian Hoitz Nov 15 '10 at 11:42
  • 14
    Actually, JSON_BIGINT_AS_STRING is [only available since 5.4](http://au2.php.net/manual/en/json.constants.php) according to the docs. For earlier versions a preg_replace solution is likely to work best as discussed below - I would do something like `json_decode(preg_replace('/("\w+"):(\d+)/', '\\1:"\\2"', $jsonString), true)` – pospi Oct 14 '11 at 04:43
20

I've resolved the issue by adding &format=json-strings to my the FQL api call, like so:

$myQuery = "SELECT uid2 FROM friend WHERE uid1=me()";
$facebook->api("/fql?q=" . urlencode($myQuery) . "&format=json-strings")

This tells facebook to wrap all the numbers in quotes, which leads json_decode to use neither int-s not floats.

Because I was afraid this issue is not restricted to FQL but to all graph API calls that choose to represent some of the IDs as BIG-INTs I've went as far as patching facebook's PHP SDK a bit to force Facebook to return all of its numbers as strings.

I've added this one line to the _graph function. This would be line 738 in facebook_base.php, version 3.1.1

$params['format'] = 'json-strings';

Sure fix

Michael Vain
  • 486
  • 3
  • 4
  • 1
    This works for those of us who aren't using the latest PHP and thus don't have JSON_BIGINT_AS_STRING. – Stan James Feb 03 '12 at 17:42
  • This was the most solid answer for me. Just in case you stumble across this later: $myQuery = $facebook->api("/fql", array( "q" => "SELECT uid2 FROM friend WHERE uid1=me()", "format" => "json-strings" )); – DavidP Jan 22 '13 at 19:06
8

I had a similar problem where json_decode was converting recent twitter/tweet IDs into exponential numbers.

Björn's answer is great if you want your BIGINT to become a string - and have PHP 5.3+. If neither of those things are true, another option is to up PHP's float precision. This can be done a different few ways...

  • find the precision value in your php.ini and change it to precision = 20
  • add ini_set('precision', 20); to your PHP app
  • add php_value precision 20 to your app's .htaccess or virtual host file
broox
  • 3,538
  • 33
  • 25
  • Simple and easy. BTW, can we adjust the number '20' so it can accommodate long value? – Ivo San May 10 '13 at 05:39
  • I add this line to my .htaccess php_value precision 20. On the first site, this works fine. But when I add this on the other site, I got 500 internal server error. What replacement should I used? – Ivo San May 14 '13 at 02:47
5

Quick and dirty, seems to work for now :

$sJSON = preg_replace('/:(\d+)/', ':"${1}"', $sJSON);
ggambetta
  • 3,312
  • 6
  • 32
  • 42
3

I use this and it works almost great.

json_decode(preg_replace('/("\w+"):(\d+)/', '\\1:"\\2"', $jsonString), true)

The json breaks when there is geo data included, eg. {"lat":54.2341} results in "lat":"54".2341

Solution:

$json = preg_replace('/("\w+"):(\d+)(.\d+)?/', '\\1:"\\2\\3"', $json);
sjngm
  • 12,423
  • 14
  • 84
  • 114
  • Thanks! That cut it for me! This problem should be fixed soon on Facebook's side though: https://developers.facebook.com/bugs/254502934666393 – Fabien Warniez Aug 28 '12 at 19:31
  • This pattern will also work since it matches digits & dots in one capture group: `preg_replace('/("[\w.]+"):([\d.]+)/', '\\1:"\\2"', $json);` – Jonathan Beebe May 07 '13 at 18:56
0

This (preg_replace('/("\w+"):(\d+)(.\d+)?/', '\\1:"\\2\\3"', $json);) worked for me (for parsing result from facebook api)

René Höhle
  • 26,716
  • 22
  • 73
  • 82
0

A major oversight on PHP's part is that browsers don't choke on integers starting with BigInt (64 bits), but before that (53 bits). The introduction of BigInt to modern browsers doesn't help much, when JSON does not support it. So I am trying to remedy that.

My approach is to handle the decoded array (before potentially re-encoding it to a string):

function fix_large_int(&$value)
 {
  if (is_int($value) && $value > 9007199254740991)
    $value = strval($value);
 }
$json_arr = json_decode($json_str, flags: JSON_BIGINT_AS_STRING | JSON_OBJECT_AS_ARRAY);
echo('before: ' . json_encode($json_arr) . '<br />' . PHP_EOL);
array_walk_recursive($json_arr, 'fix_large_int');
echo('after:  ' . json_encode($json_arr) . '<br />' . PHP_EOL);

The number 9007199254740991 is what JavaScript has as its Number.MAX_SAFE_INTEGER value. Read about that here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER

(By the way, I got here, because Twitter API's stringify_ids parameter for blocks/ids and friends/ids does not work for me. There seems to be no mention of that anywhere I could find, though.)

Alien426
  • 1,097
  • 10
  • 12