11

Our PHP API outputs results using json_encode with JSON_NUMERIC_CHECK enabled, which is important for financial figures, binary values etc.. but we have recently introduced a phone number field, and it is removing zeros from the beginning of the number when it casts it as an integer.

I've tried to force "(string)" type on the value before it goes to json_encode, but it looks like the function is overriding it. eg:

$output = array(
    'is_bool' => 1,
    'total_amount' => '431.65',
    'phone_number' => (string) '0272561313'
);
echo json_encode($output,JSON_NUMERIC_CHECK);

Produces:

{"is_bool":1,"total_amount":431.65,"phone_number":272561313}

Any suggestions on how to best handle this would be much appreciated. I really don't want to have to resort to adding trailing spaces at the end of the phone number when it's output just to get it through as a string.

Thanks!

schmoove
  • 493
  • 1
  • 4
  • 16
  • It would help us reproduce your code if you include a simple example. – Sverri M. Olsen Jul 02 '14 at 00:03
  • Why don't you share the piece of code you have tried? – Alwin Jul 02 '14 at 00:04
  • This suggestion could be an option, but I'd prefer not to have to evaluate every value I'm outputting http://stackoverflow.com/a/24365425/2789654 – schmoove Jul 02 '14 at 00:06
  • I could also format and store the phone number as per http://stackoverflow.com/a/4708314/2789654 which would avoid the problem – schmoove Jul 02 '14 at 00:14
  • "important for financial figures" -- on the contrary, the floats will mangle your numbers. – mpen Nov 15 '17 at 01:27
  • @mpen an interesting and possibly important comment. Why? A new answer to this question might be a good idea, if what you say is in fact true – KayakinKoder Mar 03 '21 at 17:44
  • @KayakinKoder See https://en.wikipedia.org/wiki/Round-off_error Floats can introduce rounding errors quite easily. Just pop open dev tools (ctrl+shift+i) and type .1*.2 in the JS console and see what you get (same problem exists in PHP). Now imagine you want to offer a 20% discount on an order, or add a 8.5% tax, or sum up dozens of line items -- suddenly your total is off by a few cents. – mpen Mar 04 '21 at 05:33
  • @mpen ok so to be clear, you're not talking about a specific problem with JSON_NUMERIC_CHECK, correct? You're talking about floats *in general*? – KayakinKoder Mar 04 '21 at 17:16
  • @KayakinKoder Not exactly. OP said JSON_NUMERIC_CHECK is important for financial figures. If they had strings representing money in their PHP array, I don't know why you'd cast them to floats and risk losing precision. Just leave them alone. If you don't need to do math on them you can print them as-is. If you *do* need to do math on them, then you're playing with fire. – mpen Mar 05 '21 at 07:05

7 Answers7

7

Instead of typecasting it to a string do something like this:

$output = array(
    'is_bool' => 1,
    'total_amount' => '431.65',
    'phone_number' => '"0272561313"' // add quotations
);

echo '<pre>';
echo json_encode($output,JSON_NUMERIC_CHECK | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);

It'l keep the trailing zero:

{
    "is_bool": 1,
    "total_amount": 431.65,
    "phone_number": "\"0272561313\""
}

Decode:

$test = json_encode($output,JSON_NUMERIC_CHECK | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
$test = json_decode($test, true);
print_r($test);

Output:

Array
(
    [is_bool] => 1
    [total_amount] => 431.65
    [phone_number] => "0272561313"
)
user1978142
  • 7,946
  • 3
  • 17
  • 20
  • 2
    Thanks for the thorough answer, but the phone_number field returned by a jQuery AJAX request returns "0272561313" with quotes, rather than just the number. Am I missing something? – schmoove Jul 02 '14 at 02:52
  • 1
    @RyanMarshall yes it is to be expected that it has quotes, since without it, the trailing and leading zeros will just be stripped again. – user1978142 Jul 02 '14 at 03:07
  • In my case this doesn't help. I still get an integer. It doesn't help whatever I wrap the phone number string into (e.g. STR053STR still returns just 53 when I encode the JSON). Any ideas? – Aleksandar Bencun May 21 '17 at 03:26
  • When I try this I get: `"phone_number": ""0272561313""` i.e. with two sets of quotes. Including JSON_UNESCAPED_SLASHES has no obvious impact. – Ade Jan 05 '18 at 12:51
7

You can try this:

{
  "is_bool": 1,
  "total_amount": 431.65,
  "phone_number": "xn#0272561313"
}

as example in mysql: SELECT CONCAT('xn#', phone_number) as phone_number...

If you used json_encode(), replace the string like this:

echo str_replace('xn#','',$your_encoded_json);
1

Had the same issue with phone numbers, this walks over an array (no objects!) to cast only numeric values not starting with 0 or + to an int or float.

        array_walk_recursive($json, function (&$value, $key)
        {
            if(is_string($value) && is_numeric($value))
            {
                // check if value doesn't starts with 0 or +
                if(!preg_match('/^(\+|0)/', $value))
                {
                    // cast $value to int or float
                    $value   += 0;
                }
            }
        });

        return json_encode($json);
Saa
  • 1,540
  • 10
  • 22
  • 1
    You can drop the `is_numeric` check since you're doing a `preg_match` anyway -- just update it to something like `/[1-9][0-9]*\z/A` Although this would still convert *some* phone numbers to ints (those not starting with 0), which is weird. – mpen Nov 15 '17 at 01:25
1

pass the data to this function to get JSON encoding with + and 0 preceded data staying as a string. I am using double encoding and str_replace to get over the issue.

public static function json_encode($data){
    $numeric = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
    $nonnumeric = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    preg_match_all("/\"[0\+]+(\d+)\"/",$nonnumeric, $vars);
    foreach($vars[0] as $k => $v){
        $numeric = preg_replace("/\:\s*{$vars[1][$k]},/",": {$v},",$numeric);
    }
    return $numeric;
}
0

works for me :

// $DATAS -> datas returned by the server
$DATAS = array(
    array('tel' => '0606060606'), 
    array('tel' => '0707070707') 
);

foreach($DATAS as $k => $v){

    $tel = " ".$v['tel']." ";  // ADD WHITE SPACE
    $DATAS[$k]['tel'] = $tel;
}

echo json_encode($DATAS, JSON_NUMERIC_CHECK);

// output : [{"tel":" 0606060606 "},{"tel":" 0707070707 "}]

Maybe define css white space or trim datas in the json encoded for obtain the perfect parsed number

-1

For what it's worth, I had a similar problem in that I have international phone numbers starting with a + symbol. I used urlencode on my phone number string to over come the issue:

$phone = urlencode( $phone );
Luke
  • 20,878
  • 35
  • 119
  • 178
-2

You no need to use

JSON_NUMERIC_CHECK

Instead use attribute-casting

https://laravel.com/docs/5.1/eloquent-mutators#attribute-casting

    class SomeModel extends Model
    {
        protected $casts = [
            'field_name' => 'array',
 'status' => 'boolean'
        ];
    }
Nur Uddin
  • 1,798
  • 1
  • 28
  • 38
  • 1
    Did you see any reference to Laravel in the question? No? Then mayyyybe mention that a full framework is needed, as well as changing the entire structure of the program to use your "fix". – DennisK Mar 01 '21 at 07:53