<?php
require_once('Services/JSON.php');
$data = '{"hoge" : 12345678901234567890}';
$json = new Services_JSON();
$decode = $json->decode($data);
var_dump($decode->hoge);
float(1.2345678901235E+19)値が数値となっているJSONデータをServices_JSONでデコードすると結果が浮動小数となり、元のJSONデータが無くなってしまいます。
仕方ないので無理矢理文字列型でデコードできないのかと思えばできません。
数値をパースしているのはServices/JSON.phpの600行目あたり、if (is_numeric($str))の分岐内です。
処理はどうなっているかというと
if (is_numeric($str)) {
return ((float)$str == (integer)$str) ? (integer)$str : (float)$str;
}
と見事に割り込む余地が無く、integerに入りきればint型、長い数値は自動的にfloat型になってしまいます。数値だとしても正確な値がほしいんだよ、という場合は無理矢理継承するかソースを直接編集するしかありません。
Services/JSON.php
//メソッドひとつ追加
function setDecodeMode($decodeMode = false){
if(isset($this->decodeMode) && $this->decodeMode){
$this->decodeMode = $decodeMode;
}
}
//Services_JSON::decode()の中を一部修正
if (is_numeric($str)) {
//デコードモードが指定してあれば
if($this->decodeMode){
if($this->decodeMode === SERVICES_JSON_IN_STR){
return $str;
}
}
// Return float or int, as appropriate
return ((float)$str == (integer)$str)
? (integer)$str
: (float)$str;
}
setDecodeMode(SERVICES_JSON_IN_STR)を呼べば数値型の場合でも文字列で返ってきます。
<?php
require_once('Services/JSON.php');
$data = '{"hoge" : 12345678901234567890}';
$json = new Services_JSON();
$json->setDecodeMode(SERVICES_JSON_IN_STR);
$decode = $json->decode($data);
var_dump($decode->hoge);
実はSERVICES_JSON_IN_STRとかは元々Services_JSON内で別の意味で使用されている定数なので、あまりこういう使い方はするべきではないのですが、まあいいや。ていうか、いいかげんPearもクラス定数使うべき。
実行すると、
string(20) "12345678901234567890"
となり無事に元の値を復元できました。
この場合元の"hoge"が数値型だったという情報は失われてしまうのですが、型が失われるのと値が失われるのとどちらがいいと言われたら前者ではないかと。
// Lookie-loo, it's a number
// This would work on its own, but I'm trying to be
// good about returning integers where appropriate:
とか書いてあるので中の人も微妙って思ってるのかね?
PR
トラックバック
トラックバックURL: