開発室ブログ

JSON Node.js PHP Python Ruby

JSONのナゾ

はじめに

APIにてJSON形式のPOSTデータをリクエストする場合、値の指定方法によってPOSTデータが無効(エラー)になるケースがあります…それはまたの機会に。

 と、前フリした通り今回はJSONについてです。短い記事ですがへぇとなる内容だと思います。

引用元:いらすとや
人気TV番組「トリビアの泉」の、あのボタンを商品化!!

 まずはJSONとは -

JSONジェイソン (JavaScript Object Notation / JavaScript オブジェクト記法) は、 JavaScript におけるオブジェクト・リテラルの部分集合によってオブジェクトや配列を表記するデータ交換書式です。

引用元:wiki.suikawiki.org/n/JSON

っということで他部署のブログにも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.

引用元:Standard ECMA-404 The JSON Data Interchange Syntax

 JSONにおける数値表現のシンタックスは以下の通りです。

引用元:Standard ECMA-404 The JSON Data Interchange Syntax

 上記の図を見ると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

json_decode()

0

-0

-0

負号優先?

Node.js

JSON.stringify()

0

0

0

数学者ファースト

python

json.loads()

0

-0.0

-0.0

明示的にデータの型(Integer,FloatまたはDouble)を意識している

ruby

JSON.parse()

0

-0.0

-0.0

同上

おわりに

 すったもんだの末、使用する言語におけるJSONデコードの仕様により、数値の扱いに違いがあるので注意が必要ということでした。上記以外の言語ではどうなんでしょう?機会があれば調べてみたいと思います。

 また今回、改めてJSONの構文について紐解き、リスト処理を髣髴とさせる面白さがありました。by 往年のLisper

おまけ

 JSONの派生仕様って下記の通りとても多いようです。

引用元:wiki.suikawiki.org/n/JSON

 その中でJSON5という怪しい派生版を見つけました。JSON1~4という派生版は無く、いきなり5が出現してきたのでどう考えてもJacks○n5のパロディと思うのは杞憂でしょうか?😁

RecentPost