WEBツール
API等のデータのやり取りは主にJSONを利用しているが、人の目で見たり比較する時はYAMLが便利です。
YAMLはエスケープされた記号(改行=「\n」等)をエスケープ前に戻したデータで表現ができるのがいいところですね。
そこで、JSON→YAML→そのまま比較するWEBツールを作りました。
https://rawseq.github.io/json-diff/
変換機能はjs-yaml,比較ツールはmonacoを利用しています。
既に他のWEBツールやLive Demoで体験済かもしれませんが、「エスケープ文字が戻らない場合があるのでは?」という不安を持っている方がいると思いますが、だまされたと思って一度使ってみてください。(そして戻らなければ教えてください><;)
※Chrome推奨。monacoの仕様でA→Bの比較、Bの編集しかできません。(編集したBをJSONでコピーすることはできます)
JavascriptでJSONからYAMLに変換する
Javascript上のJSON YAML変換は特殊記号類の変換がくせものです。
JSON-YAML変換は「js-yaml」以外にもいろいろあるのですが、特殊文字が含まれる場合に動作不良になるケースが見受けられました。その中でも一番有望に見えたのが「js-yaml」でした。
しかしながら、「js-yaml」でも理想の動作をしてくれない場合があります。
例えばのJSONをYAMLに変換しようとすると
{"data":"\u6539\u884c\uff1a\n\u30bf\u30d6\uff1a\t\n\u30b9\u30e9\u30c3\u30b7\u30e5\uff1a/\n\u30d0\u30c3\u30af\u30b9\u30e9\u30c3\u30b7\u30e5\uff1a\\\n\u30b7\u30f3\u30b0\u30eb\u30af\u30a9\u30fc\u30c8\uff1a'\n\u30c0\u30d6\u30eb\u30af\u30a9\u30fc\u30c8\uff1a\"\n\u30d0\u30c3\u30af\u30af\u30a9\u30fc\u30c8\uff1a`\n\u30c9\u30eb\u8a18\u53f7\uff1a$\n\u30a2\u30c3\u30c8\u30de\u30fc\u30af\uff1a@\n\u4e2d\u62ec\u5f27\uff1a{}\n\u30b3\u30ed\u30f3\uff1a:\n\u7d75\u6587\u5b57\uff1a\u270c\n\u591a\u91cd\u30a8\u30b9\u30b1\u30fc\u30d7\u3055\u308c\u305f\u30bf\u30d6\uff1a\\t"}
こんな感じになります。
data: "改行:\nタブ:\t\nスラッシュ:/\nバックスラッシュ:\\\nシングルクォート:'\nダブルクォート:\"\nバッククォート:`\nドル記号:$\nアットマーク:@\n中括弧:{}\nコロン::\n絵文字:✌\n多重エスケープされたタブ:\\t"
これではたとえ仕様上問題ないとしても、比較や目視では使えません。
そこで、修正を加えた後に変換したYAMLがこちら
data: |-
改行:
タブ:
スラッシュ:/
バックスラッシュ:\
シングルクォート:'
ダブルクォート:"
バッククォート:`
ドル記号:$
アットマーク:@
中括弧:{}
コロン::
絵文字:✌
多重エスケープされたタブ:\t
きれいにエスケープが戻るようになりました。
js-yamlでエスケープが戻らなかった原因は「\t」タブコード。
YAML変換前にタブを別な任意文字に変換。変換後に任意文字から戻す処理を加えています。
多重エスケープのタブを除外しているので少し歪になっていますが、
/**
* JSON to YAML
* @param string val 入力
* @return string 出力
*/
function jtoy(val){
try{
return jsyaml.dump(
JSON.parse(
val
.replace(/([^\\])\\t/g,"$1<<TAB>>")
.replace(/([^\\])\\t/g,"$1<<TAB>>")
.replace(/^\\t/g,"<<TAB>>")
)
).replace(/<<TAB>>/g,"\t");
}catch(e){
alert(e.message);
}
}
おまけ(YAML⇒JSON変換時の文字列エンコード)
JavascriptのJSONオブジェクトやMDNのポリフィルではUnicode文字列のエンコードがかからなかったので、つけてみました。
/**
* YAML to JSON
* @param string val 入力
* @return string 出力
*/
function ytoj(val){
try{
return unicodeEscape(JSON.stringify(jsyaml.load(val)));
}catch(e){
alert(e.message);
}
}
function unicodeEscape(str){
var code, pref = {1: '\\x0', 2: '\\x', 3: '\\u0', 4: '\\u'};
return str.replace(/[^\u0020-\u007e]/g, function(c) {
return pref[(code = c.charCodeAt(0).toString(16)).length] + code;
});
}