2
0

More than 1 year has passed since last update.

JSONが正しい文法なのにJSON.parseできない

Last updated at Posted at 2022-03-02

JSONの文法と、PHPの文法(今回あまり関係ない)と、JavaScriptの文法と、HTMLの文法とでごちゃごちゃに怒られたのでどの部分が明確な原因かわかっていないけど順番にメモする。

はじめに

APIから吐き出されたJSONをPHPで取得し、それをJavaScriptで加工・処理してHTML上に表示するという組込み作業の過程で、文法チェッカーなどでもJSONの構造に問題はなさそうなのに、JSON.parseがエラーになる。

検索すると上のような記事が出る。console.logに表示されないが特殊文字がエラーの原因になってるよという話。
上記のコードで対策してみたがそうするとJSON内の改行コードも漏れなく消え失せてしまったので、ちょっと本意でない。HTML上で表示する観点から改行コードには消えてもらうのではなく、最終的に改行タグになってもらう必要がある。別の方法を考える。
(いま思うと二つ目のメソッドから書き始めたせいかもしれない。なんでそんなことしたんだろう)

APIから吐き出されるJSONについても別サーバー上のPHPで自前で整形しているので、その時点から調整できそうだ。

json_encode()のオプション

https://www.php.net/manual/ja/function.json-encode.php
https://www.php.net/manual/ja/json.constants.php

APIのJSON出力前にjson_encode()を通しているが、その時にオプションをつけると適宜エスケープを調整できる!関係あるか?

api-変更前.php
echo json_encode($list,JSON_UNESCAPED_UNICODE);
api-変更後.php
echo json_encode($list,JSON_UNESCAPED_UNICODE | JSON_HEX_QUOT | JSON_HEX_APOS);


JavaScriptでパースエラーになった。なんで???

渡ってきたJSONを受け取り側のPHPで処理するところまでは良いものの、その後JavaScriptに受け渡す際は文字列として扱うことになる。その際、例えばダブルクォーテーションがエスケープされていても、「\u0022」も「"」も文字列であることに変わりはないため、JavaScriptの文法で「そこが文字列の終わり」と見なされてしまうらしい。
(いま思うとJSONデータにこの時点で新たに"を追加して検証していたので、先程と異なる理由でエラーが出ていた可能性アリ)
これに関する記事も見つけたけど見失いました。

JSON自体のエスケープ不足にくわえ、JSONの中身がJavaScript上の文法のエラーにひっかかって先に進めない問題も出てきた。わからん。

見かけた記事の対策に、JavaScript側でJavaScript用のエスケープ処理を行うことが挙げられていたが、二度手間になりそうなのでなんとかPHP側でうまい事できないか。

replace()

PHP側かつJavaScriptで文法エラーにならないエスケープ処理として、文字参照に変換することにした。

replace.php
  $es_val = str_replace('\\', '\', $es_val);
  $es_val = str_replace('"', '&quote', $es_val);

こんな風に地道にやっていたけどそういえばもう少し使いやすい関数があることを途中で思い出した。

htmlspecialcharsとnl2br

htmlspecialcharsは「& (アンパサンド)、" (ダブルクォート)、' (シングルクォート)、< (小なり)、>(大なり)」をエンティティ参照に変換する関数。
nl2brはあらゆる改行コードの手前に改行タグを挿入する関数(そのためその後改行コードを置換削除する必要がある)。

escape.php
function escape($es_val){
  $es_val = htmlspecialchars($es_val);
  $es_val = nl2br($es_val);
  $es_val = str_replace(array("\r\n", "\r", "\n"), '', $es_val);
  $es_val = str_replace('\\', '&bsol;', $es_val);
  return $es_val;
}
escape($value);

あんまり分かっていなかったが、結果的にJSON内の特殊文字を削除することでパースエラーの生じないJSONを生成できたらしい。
単にエスケープして残すよりも、文字参照に変換したり削除したりしたほうがHTML上で取り回しの効くデータになったかも。

余談

Vue.jsとの併用

Vue.js内のデータにタグが含まれていても自動的に無効化され、そのままマスタッシュで出力しようとすると、タグが文字列で出力されてしまう。
v-htmlで出力する必要アリ。
文字参照(&で始まる特殊文字)もマスタッシュではそのまま出力されるので、同じくv-htmlを使う必要がある。
ちょっと難点。

JSONでエスケープすべき特殊文字

『"(ダブルコーテーション)』
『(バックスラッシュ)』
『/(スラッシュ)』
『\b(バックスペース)』
『\f(改ページ)』
『\n(改行)』
『\r(キャリッジリターン)』
『\t(タブ)』

らしい。
ただ今回のJSONはフォーム入力によって生成されたテーブルデータを元に出力・整形する代物なので、バックスペース、改ページ、タブは含まれないだろうと判断した。
そのためダブルコーテーション、バックスラッシュ、改行、キャリッジリターンをエスケープする必要があった。
なおスラッシュに関してはエスケープなしで問題なかった。なんでかわからん。

2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0