原因:エスケープされていなかったから
『"(ダブルコーテーション)』
『\(バックスラッシュ)』
『/(スラッシュ)』
『\b(バックスペース)』
『\f(改ページ)』
『\n(改行)』
『\r(キャリッジリターン)』
『\t(バックスペース)』
JSONでは上記の文字を含めるときにエスケープしなければいけません。プログラムのソースコードとかを記事に書くと""や/などはよく使うので引っかかり安いです。
そこまではまあ当たり前なのですが、今回引っかかったのはUrlエンコードとの兼ね合いでした。
"あ"
という文字列を$.ajaxで送信する時に、先にencodeURIComponent()でUrlエンコードし、その後JSON.stringify()でStringにしたものをPHP側に渡し、urldecode()しjson_decode()すると失敗します。
これはurldecodeした時のJSONが{"data":""あ""}みたいになってしまうからです。
$.serialiseでフォームのデータを取得し、それを加工したものをJSON.stringify()して送信したらこうなりました。
通常はJSON.stringify()がエスケープ文字を追加してくれますが、Urlエンコードされた文字は%22みたいになっておりエスケープ文字ではないのでエスケープされずにPHP側に渡ってしまうわけです。
なので私は$.serialise($.serializeArray)で取得したデータをdecodeURIComponent()してからJSON.stringify()でエスケープ&JSON化してencodeURIComponent()でUrlエンコードし直してPHPに渡しました。
そこでふと疑問に思ったのはajax通信でもUrlエンコードって必要なのだろうか?ってことですが、やっておいて損はなさそうなのでやっておきました。