php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #68456 json_decode should be able to treat all numbers as strings
Submitted: 2014-11-19 22:18 UTC Modified: 2015-06-21 13:36 UTC
Votes:8
Avg. Score:4.1 ± 0.8
Reproduced:7 of 7 (100.0%)
Same Version:4 (57.1%)
Same OS:4 (57.1%)
From: admin at bittylicious dot com Assigned: bukka (profile)
Status: Wont fix Package: JSON related
PHP Version: 5.4.35 OS: Linux Wheezy
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2014-11-19 22:18 UTC] admin at bittylicious dot com
Description:
------------
There should be an option that can be passed to json_decode that will allow numbers to be handled as strings. This is because some numbers can not be represented in a float properly. In reality, JSON is often used as a transport layer, and it's very useful to keep this accuracy.

One example in practice where this has caused issues is when dealing with Dogecoin (a Bitcoin derivative). Dogecoin has eight decimal places, and because each Dogecoin is worth only a tiny fraction of a cent, they can often be represented as tens or hundreds of millions of Dogecoins. When you're dealing with finance, it's essential not to lose precision. The only workaround I have is to use a complicated preg_replace to add quotes to the original JSON string, which is nasty.

Test script:
---------------
<?php
print_r(json_decode('{"test":1234567890.12345678}'));
?>

Output:

stdClass Object
(
    [test] => 1234567890.1235
)



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-03-15 16:45 UTC] bukka@php.net
-Assigned To: +Assigned To: bukk
 [2015-03-15 16:45 UTC] bukka@php.net
-Assigned To: bukk +Assigned To: bukka
 [2015-03-15 16:53 UTC] bukka@php.net
-Status: Assigned +Status: Wont fix
 [2015-03-15 16:53 UTC] bukka@php.net
The problem is that this number will never get saved with the full precision. You can influence the number of visible decimal places with setting ini precision as it is in 

http://3v4l.org/80iCh

However as you can see the number is rounded because the internal float representation (C double type) cannot contain the whole number. So this is not JSON related. and any conversion to string can't fixed. The thing is that json_decode is using normal string conversion.

So this can't be fixed in the JSON. I'd recommend you not to save the number to the the float at all but represent it as a string.
 [2015-03-15 17:27 UTC] admin at bittylicious dot com
That's my point precisely though - I don't want to save the number as a float, I want to save it as a string. That way, I can either record it somewhere or do some bcmath on it.

JSON is really regularly used as a transport stream where data isn't manipulated. In my real life example, I'm passing one value, via JSON, to a totally different process. I don't do any math on the number at all, but the precision is lost. This is why I want to treat it as a string but it's impossible to do this without manipulating the JSON itself before passing it to json_decode.

Please reopen this if you agree.
 [2015-03-15 18:16 UTC] bukka@php.net
The problem is that the precision will be lost anyway. Most of all you loose the precision immediately when you save it as a float. Basically this:

$a = 1234567890.12345678;

will loose the precision. You will be never able to get it back. I hope that this example will explain it a bit more:

http://3v4l.org/r2uGa

Mind that string conversion in json would do the same thing as using (string). Otherwise it wouldn't respect precision ini. 

Can you see what I mean now?
 [2015-03-15 19:42 UTC] admin at bittylicious dot com
I do understand what you're saying, but my feature request is under the assumption you never ever need to convert this to a float, and my real life use case is indeed this situation.

Simplifying things, let's imagine a JSON service that simply multiplies a number by two. You would pass in:

{"val":1234567890.12345678}

But you would have no way to even pass this to bcmul (which I believe works on strings) without losing precision even if bcmul needn't have any precision issues.

Consider another service. This simply forwards "val" onwards perhaps with a hash signature or something. Consider this a "signing service". It cannot read this 'val' value without losing precision, when I think it should be possible to interpret this as a string. This is actually more similar to my use case, in other words, using JSON really as a transport mechanism and not manipulating these numbers within the PHP script.
 [2015-03-15 20:06 UTC] bukka@php.net
-Status: Wont fix +Status: Re-Opened
 [2015-03-15 20:06 UTC] bukka@php.net
That makes sense. I'm a bit dump so it took me a bit... :)

I actually recently closed something similar for encode so the constant could be used in both ways. I'd imagine something like JSON_DOUBLE_TO_STRING . I'll try to put together a quick RFC for 5.6 up (with choice for 7 only) next week and if it gets in, I will do the patch.
 [2015-03-15 21:17 UTC] admin at bittylicious dot com
Fab stuff, thanks so much for examining this. Great to hear that the encode functionality is also under consideration too.
 [2015-04-19 19:09 UTC] bukka@php.net
Just a quick update. We had a discussion about it on internal list: http://bit.ly/1D2ZyAR . I also started RFC to propose addition of float and int to string flags: https://wiki.php.net/rfc/json_numeric_as_string .

However I have been thinking about that a lot and I don't think it would be a proper way how to fix this. Such flag would influence all float values and it's limited only to string.

I think that better solution would be to introduce support for json schema validation that could be also extended as a sort of type hints for supplied json. Then it would be possible to select just specific values and use whatever types (e.g. objects or string). It's a relativaly bigger feature so it might take a bit longer but I think that it's a proper way of fixing this.
 [2015-04-19 19:32 UTC] admin at bittylicious dot com
Thanks for discussing this on the mailing list - good to hear there are some positive comments too.

I still think this, simpler version should be implemented. We already have JSON_BIGINT_AS_STRING so it seems as though it could be argued as plugging a missing option rather than a full on enhancement as such. Going the whole hog and allowing object hints etc., is far larger than this; having this fix go in means that users can implement "casting" to objects manually, but I have no even remotely-nice workaround I can use to handle these float values at all at present.
 [2015-04-19 22:12 UTC] yohgaki@php.net
Related bug

https://bugs.php.net/bug.php?id=69422
 [2015-04-22 17:26 UTC] bukka@php.net
As I said I don't think that adding new flags is the best solution but it's also true that properly implementing json schema will take much longer and it's a relatively big feature.

Ok then I will let the PHP members decide. It means that I will proceed with the RFC. I will need to add more info into it but will probably put it  under discussion in the next few weeks.
 [2015-04-22 18:07 UTC] admin at bittylicious dot com
Thanks very much for that. I certainly think the severity warrants it considering there is no real workaround.
 [2015-06-21 13:36 UTC] bukka@php.net
-Status: Re-Opened +Status: Wont fix
 [2015-06-21 13:36 UTC] bukka@php.net
The RFC has been rejected and there won't be any json_decode flag that would decode all numbers as strings.

https://wiki.php.net/rfc/json_numeric_as_string#voting

The more generic solution (e.g. JsonSchema based) is preferred.
 [2015-06-23 08:19 UTC] admin at bittylicious dot com
OK, thank you for taking this as far as you at all were able to do so.

I still disagree with the outcome (as you may imagine) as this is a feature request that borders on a bug, but the elders have spoken.

Thanks again.
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Mon Feb 24 03:01:25 2020 UTC