JSONのナゾ
はじめに
APIにてJSON形式のPOSTデータをリクエストする場合、値の指定方法によってPOSTデータが無効(エラー)になるケースがあります…それはまたの機会に。
と、前フリした通り今回はJSONについてです。短い記事ですがへぇとなる内容だと思います。
まずはJSONとは -
JSONジェイソン (JavaScript Object Notation / JavaScript オブジェクト記法) は、 JavaScript におけるオブジェクト・リテラルの部分集合によってオブジェクトや配列を表記するデータ交換書式です。
っということで他部署のブログにもJSONについての記事が投稿されていますので、そちらも併せてご参照願います。
データ消滅
或る日、APIのテスト中にPOSTメソッドの入力データとしてJSON形式でリクエストすると、データ無しのエラーが発生しました。正しく入力データを指定したハズですが、API側の入力チェックではデータが届いていないようでした。
以下のようにJSON形式でデータを指定
{"val" : 01}
API側のPOSTデータ
Array
(
)
理由はJavaScriptにおいて0から始まる整数は8進数として解釈しています。JavaScriptにおけるオブジェクト・リテラルの部分集合であるJSONではStandard ECMA-404 The JSON Data Interchange Syntaxの規約に基づき0から始まる整数はシンタックスエラーとし、10進数以外は数値として扱わないとしています。
A number is a sequence of decimal digits with no superfluous leading zero.
JSONにおける数値表現のシンタックスは以下の通りです。
上記の図を見ると0の次に来る文字はdigit(数字)ではなく小数点の.
(ドット)または指数部の始まりを示すe
(またはE
)であり、それ以外は許容していません。従って明示的に0から始まる数値データを送る場合は
{"val" : "01"}
API側のPOSTデータ
Array
(
[val] => 01
)
のように文字列として渡します。(JavaScript❤なOちゃん、これで問題ないですよね?)
0と-0
数値の指定方法によって評価の結果が異なることがあるようなので検証してみましょう。
検証用コード(json.php)PHP 7.2.4 (cli) (built: Mar 27 2018 16:16:09) ( NTS )
<?php
print_r(json_decode ('{"val":'.$argv[1].'}'));
負号付整数( { “val” : -0 } )
$ php json.php -0
stdClass Object ( [val] => 0 )
負号付小数( { “val” : -0.0 } )
$ php json.php -0.0
stdClass Object ( [val] => -0 )
負号付指数( { “val” : -0e0 } )
$ php json.php -0e0
stdClass Object ( [val] => -0 )
-0
、-0.0
、-0e0
は数学的には同じ0
なのですが、上記のように二通りの評価結果があり、この結果によってはバリデーションでエラーになる恐れがあります。
例えば、バリデーションルールにCodeigniterのin_list[0,1]
やexact_length[1]
を定義して、POSTデータに-0
を指定してもエラーにはなりませんが、-0.0
を指定した場合はエラーになります。
つまり0の前に負号があり
- 0の後ろに文字が無い
- 0の後ろに許容文字(
.
またはe
,E
)がある
によって評価結果が異なることから、JSONにおける数値の評価は小数部及び指数部の評価前に負号を決定していると愚考してます
が…
ただ単純にPHPの
json_decode()の仕様では?
という疑惑が頭をもたげてきましたので、取り急ぎ動作可能なPHP以外の言語でも確認してみます。
他言語におけるJSONデコードの挙動
まずはサーバサイドで動くJavaScript(Node.js)で確認します。v11.4.0
> console.log(JSON.stringify({"val1":-0,"val2":-0.0,"val3":-0e0}));
{"val1":0,"val2":0,"val3":0}
これ(python)はど?Python 3.6.8 (default, Apr 25 2019, 21:02:35)[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux
> > > import json
> > > print(json.loads('{"val1":-0,"val2":-0.0,"val3":-0e0}'))
{'val1': 0, 'val2': -0.0, 'val3': -0.0}
で最後にコレ(ruby)ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
require 'json'
p JSON.parse('{"val1":-0,"val2":-0.0,"val3":-0e0}')
{"val1"=>0, "val2"=>-0.0, "val3"=>-0.0}
なるほど…
まとめるとこのようになりました
言語\入力値 |
-0 |
-0.0 |
-0e0 |
考察 |
PHP |
0 |
-0 |
-0 |
負号優先? |
Node.js |
0 |
0 |
0 |
数学者ファースト |
python |
0 |
-0.0 |
-0.0 |
明示的にデータの型(Integer,FloatまたはDouble)を意識している |
ruby |
0 |
-0.0 |
-0.0 |
同上 |
おわりに
すったもんだの末、使用する言語におけるJSONデコードの仕様により、数値の扱いに違いがあるので注意が必要ということでした。上記以外の言語ではどうなんでしょう?機会があれば調べてみたいと思います。
また今回、改めてJSONの構文について紐解き、リスト処理を髣髴とさせる面白さがありました。by 往年のLisper
おまけ
JSONの派生仕様って下記の通りとても多いようです。
その中でJSON5という怪しい派生版を見つけました。JSON1~4という派生版は無く、いきなり5が出現してきたのでどう考えてもJacks○n5のパロディと思うのは杞憂でしょうか?😁