I'm trying to integrate with some Partner API.
They only accept json with float type for amount.
Example:
- OK
{"amount":0.0000005}
- Error
{"amount":"0.0000005"}
{"amount":5.0E-7}
If the value greater or equal 1, then it's OK scenario always. But in my case I have values > 0, and < 1.
Code Example:
$arr = ['amount' => 0.0000005];
$str = json_encode($arr);
echo $str;
Output:
{"amount":5.0e-7}
I want the output to look like this:
{"amount":0.0000005}
Is it possible in php? May be some hacks & tricks?
CodePudding user response:
The cleanest I can think of is to traverse through the data, recursively substituting small numbers with a placeholder; then, after JSON encoding, replace the placeholders in the final JSON string with the number formatted how you want it.
The surprisingly difficult part is formatting the float itself; I found this existing question about how to do that with some working but not very elegant implementations. For brevity, I've left that part as a TODO below.
class JsonMangler
{
private const THRESHOLD = 0.0001;
private const PLACEHOLDER = '__PLACEHOLDER__';
private array $mangledData = [];
private array $substitutions = [];
private int $placeholderIncrement = 0;
public function __construct(array $realData) {
// Start the recursive function
$this->mangledData = $this->mangle($realData);
}
private function mangle(array $realData): array {
$mangledData = [];
foreach ( $realData as $key => $realValue ) {
if ( is_float($realValue) && $realValue < self::THRESHOLD) {
// Substitute small floats with a placeholder
$substituteValue = self::PLACEHOLDER . ($this->placeholderIncrement );
$mangledData[$key] = $substituteValue;
// Placeholder will appear in quotes in the JSON, which we want to replace away
$this->substitutions["\"$substituteValue\""] = $this->formatFloat($realValue);
}
elseif ( is_array($realValue) ) {
// Recurse through the data
$mangledData[$key] = $this->mangle($realValue);
}
else {
// Retain everything else
$mangledData[$key] = $realValue;
}
}
return $mangledData;
}
/**
* Format a float into a string without any exponential notation
*/
private function formatFloat(float $value): string
{
// This is surprisingly hard to do; see https://stackoverflow.com/q/22274437/157957
return 'TODO';
}
public function getJson(int $jsonEncodeFlags = 0): string
{
$mangledJson = json_encode($this->mangledData, $jsonEncodeFlags);
return str_replace(array_keys($this->substitutions), array_values($this->substitutions), $mangledJson);
}
}
Using this implementation for formatFloat
, the following test:
$example = [
'amount' => 1.5,
'small_amount' => 0.0001,
'tiny_amount' => 0.0000005,
'subobject' => [
'sub_value' => 42.5,
'tiny_sub_value' => 0.0000425,
'array' => [
1.23,
0.0000123
]
]
];
echo (new JsonMangler($example))->getJson(JSON_PRETTY_PRINT);
Results in the following output:
{
"amount": 1.5,
"small_amount": 0.0001,
"tiny_amount": 0.0000005,
"subobject": {
"sub_value": 42.5,
"tiny_sub_value": 0.0000425,
"array": [
1.23,
0.0000123
]
}
}
CodePudding user response:
The only way to keep it the long way is to convert it to a string instead. But then it is not a number anymore!
$arr = ['amount' => number_format(0.0000005, 7)];
$str = json_encode($arr);
giving
{"amount":"0.0000005"}
Javascript itself would use the scientific notation:
j = {"amount":"0.0000005"};
parseFloat(j.amount);
5e-7
A hack would be to remove the quotes.
$quoteless = preg_replace('/:"(\d .\d )"/', ':$1', $str);
echo $quoteless;
will give
{"amount":0.0000005}