PHPで作成した配列データをフロントのJavaScriptに渡したいとき、PHPの配列をそのまま渡すことはできないのでJSON形式のデータに変換します。
PHPで配列をJSON形式のデータに変換するときはjson_encodeを使いますが、エンコード後のJSONをJavaScriptのJSON.parse()に食わせるとシンタックスエラーになるケースがありました。
ひとまず解決したので備忘録。
JSON.parseで発生した問題
まず、発生した問題について説明します。解決法のみ知りたい方は飛ばしてください。
- PHPでDBから取得したデータの連想配列をフロントのJavaScriptに渡したかった
- 連想配列をそのまま渡せないのでJSONの形にする必要がある
- JavaScriptでは受け取ったJSONをオブジェクトに展開して使いたい
PHPで配列データをJSON形式に変換するにはjson_encodeを使います。
// PHP
$dataArray = array('id' => '1', 'name' => 'taro');
$jsonString = json_encode($dataArray);
JavaScriptでJSON文字列をオブジェクトに展開するのはJSON.parseです。
// JavaScript
const obj = JSON.parse('<?php echo $jsonString ?>');
// JSON.parseすることでobjのプロパティにデータが展開される
この実装でよしとしていたんですが、当該ページが動かないと報告を受け、実際にその時のデータで再試すると確かに動かない。
ブラウザのデバッガで確認すると、JSON.parseしている行でJavaScriptのシンタックスエラーが発生していました。
原因は特殊文字
JSON.parseでエラーが発生した原因は、JSON文字列内に特殊文字が入っていたためでした。
JSONを扱うときにはJSON文字列として正しい形かの他に、特殊文字がHTMLやJavaScriptの構文解釈に影響を与えないか注意しなくちゃいけない。
// PHP
$dataArray = array();
$dataArray += array('id' => '1', 'name' => 'ta"'"ro');
$dataArray += array('id' => '2', 'name' => 'ta"ro');
$jsonString = json_encode($dataArray);
上のコードはちょっと無理やりですが、$dataArrayのnameの中に’(シングルクォーテーション)や”(ダブルクォーテーション)が入っていて、JSON.parseするとエラーになります。
「’」や「”」はJSONで特別な意味がある特殊文字なので適切なエスケープが必要です。
JSONの特殊文字をエスケープする
PHPのjson_encodeで特殊文字をエスケープするには、以下のようにオプションを指定します。
// PHP
$jsonString = json_encode($dataArray, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
JSON_HEX_TAG
<を¥u003C、>を¥u003Eに変換するオプション。
JSON_HEX_AMP
&を¥u0026に変換するオプション。
JSON_HEX_APOS
‘を¥u0027に変換するオプション。
JSON_HEX_QUOT
“を¥u0022に変換するオプション。
これらのオプションを指定してjson_encodeすれば一部を除いてJSON.parseのエラーは解消するはず。
なぜ一部なのかというと、今回僕が遭遇したケースでは解消しなかったためです。
その原因と解決策を以下説明します。
特殊文字をエスケープしてもJavaScriptでエラーになる場合
僕が遭遇したケースでは”(ダブルクォーテーション)をJSON_HEX_QUOTオプションで¥u0022に変換してもJSON.parseのシンタックスエラーは消えませんでした。
多分JavaScriptとして解釈しようとすると、文中に急に「”」が出てきたから、そこまでが文字列として認識されてしまっている。これについても問題ないようエスケープする必要があります。
それを踏まえてPHP(JSON生成側)の最終形コードは以下の形となりました。
// PHP
$jsonString = json_encode($dataArray, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
$jsonString = str_replace('¥u0022', '¥¥¥"', $jsonString);
「”」を「¥”」でエスケープしています。
JSON_HEX_QUOTが冗長なので直接「”」を置き換えたほうがいいかも。