今さらJSONの仕様を再認識した話

Date:

Share post:

Web系プログラミングをしていると構造化されたデータ(配列等)をJSONに変換して扱うケースが結構あります。
ただ、JSONを直接書くということは稀で、プログラムによるエンコード結果がログやDBに登録された状態を目視で確認するような時しか触れる機会はないかと思います。

しかし、先日とある事情でDBに登録されているJSONデータを別の内容に書き換える必要がありまして、一見正しそうに変更してみたものの期待した結果が得られないということがありました。
当然ながら原因はJSONの書式が間違っていただけだったのですが、改めてJSONの仕様に関して正確に把握していないということを思い知ったので、今回はその辺を書いてみたいと思います。

そもそもJSONとは

JSONの仕様云々以前に、そもそもJSONって何?ということ自体考えたことがなかったので確認してみたところ「JavaScript Object Notation」という意味だったんですね。

個人的にはバックエンドエンジニアなので当然ならがフロントエンド(つまりはJavaScript)との間でJSONエンコードされたデータの送受信を行うと言うことはありますが、先に書いたように構造化されたデータをDBに登録するようなケースでもJSONを使うので、あまりJavaScriptに絡めて考えたことはなかったのですが。

JSONの注意すべき仕様

JSONの仕様全般に関してはGoogle先生に聞いてもらうとして、ここでは特に関連するプログラミング言語(PHP, JavaScript)の仕様との違いで間違い易いものを上げておきたいと思います。

文字列はダブルクォーテーションで囲む

今回、手書きでJSONを書き換えた際のミスがこれだったのですが、JSON内での文字列はダブルクォーテーションで囲む必要があり、シングルクォーテーションではダメなんですね。

PHPやJavaScriptでは文字列を表記する場合はダブルクォーテーションでもシングルクォーテーションも問題ないので(若干の違いはありますが)、その感覚でシングルクォーテーションを使うとハマってしまうという話です。

true, false, null は小文字で書く

PHPの場合、true, false, nullは大文字で書いても小文字で書いても(加えてアッパーキャメルTrue, False, Nullで書いても)問題ないですが、JSONでは小文字で書かないとダメとのことです。

配列は角かっこ、オブジェクト(連想配列)は波かっこで囲む

JavaScript的には当然なのかもしれませんが、PHPでは普通の配列でも連想配列でも角かっこで囲むので、PHP使いは気をつけた方が良いです。

json_decodeの仕様

PHPにおいてJSONの書式不正に気づき難い要因としてはjson_decodeの仕様も関係しているように思います。

json_decodeは文字通りJSONをデコードするPHPの関数ですが、エラーの場合は基本的にはnullを返す仕様になっています。当然ながらデコード対象として指定されたJSONの書式に不正があった場合もnullが帰ります。
個人的にはここが疑問です。

json_decodeと対をなすjson_encodeでnullをエンコードすると、戻り値は文字列”null”です。
一方で文字列”null”をjson_decodeでデコードするとnullが返されます。
このエンコード・デコード結果に矛盾はありません。
しかし、これはjson_decodeよる正当なデコード結果としてnullが返されるケースがあると言うことを意味します。
となると、単純にjson_decodeの戻り値がnullだったからと言ってエラー扱いする訳にはいかないと言うことになります。

実のところ、json_decodeによる正当なデコード結果としてnullが返されることがあることは認識していたので、json_decodeの処理内でエラーを検出した場合は例外が投げられるものと勝手に思っていました。
言い換えれば、json_decodeのようにnullやfalseのような一般的にはエラーを意味する値を正当な戻り値として返す場合がある関数では、エラー発生を通知するためには当然のごとく例外が投げられるものと思っていました。
よって、今回問題となったJSONの処理においては、書式に問題があってnullが返された場合でもそのnullを正当なデコード結果として扱うようになっていたためエラーにはならず、問題に気づき難かったという状況でした。

PHP7.3.0以降であれば、json_decodeの第四引数にJSON_THROW_ON_ERRORなるフラグを設定するとエラー発生時に例外を投げるようになっているようです。
この例外を投げる仕様をデフォルトにしてもらった方が個人的なフィーリングには合うのですが、どうでしょう?

所感

結局のところはJSONの書式にしろjson_decodeの仕様にしろ、しっかり確認せずに使ったことが根本的な問題です。
ただ、もともと紛らわしい仕様でなければ勘と経験ベースでも使えていたであろうと思う部分もあり、正直「罠にハマった」感もなきにしもあらずです。

自分の先入観をどれだけ疑えるかという話かとも思いますが、それが難しいんですけどね…



Related articles

LaravelでPDF生成(mpdf)

EC関連のシステムなどでは請求書や領...

我流Flutter学習ステップ(6)スマホでの動作...

本シリーズ(?)の最初の投稿で書いた...

その「平均値」に意味はあるのか?

最近「平均」に関して思うところがあっ...

遅まきながら、Vagrant(Virtualbox...

前回は、pumaを常時起動のユーザーサ...