素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

Firefox3のnightlyでサポートされたJSON.parseとJSON.stringify

JSON.parseとJSON.stringifyがサポートされた。
これは、JSON in JavaScriptのネイティブ実装と言える。

これからWebサイトでJSONを使う時は、

<head>
  <script type="text/javascript">
    if (typeof JSON != "object")
      document.write('<script type="text/javascript" src="http://www.json.org/json2.js"></script>'); // 実際にはローカルに落したものを使う
  </script>
  <script type="text/javascript">
    // JSON.parseやJSON.stringifyを使う処理
  </script>
</head>

とすると、クロスブラウザで、なおかつ最新のブラウザだとネイティブに動くようになる。

使いかた。
基本的にはjson2.jsと同じ。

AutoPagerizeのjsonを取ってきてパースする場合、

var url = "http://wedata.net/databases/AutoPagerize/items.json";

var xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.onload = function () {
  var obj = JSON.parse(this.responseText);
  console.log(obj);
};
xhr.send(null);

のようにできる(wedata.net上でFireBugで実行)。
今までも、evalを使って、eval("(" + jsonStr + ")");とすることでパースできたけど、この場合はJSON以外の危険な物も実行されてしまうので、あまりよくなかった。

JSON.parseだと、JSON以外の文字列は弾いてくれるので、安全。

var str = "alert('unsafe')", obj;

obj = eval("(" + str + ")"); // unsafe

try {
  obj = JSON.parse(str);
} catch (err) {
  alert(err); // Error parsing JSON.
}

JSON.stringifyは、obj.toSource()やobj.toString()、uneval(obj)と似たような物で、厳密なJSON文字列に変換してくれる。

JSON.stringify({a: 1}); // {"a":1}

のように使う。
unevalは、evalで実行できるJavaScriptオブジェクトに変換するものだから、

uneval({a: 1}); // ({a:1})

のように、出力が少し違う。

JSON.stringifyに変なものを渡すと、

JSON.stringify(window); // {"console":{},"_firebug":{},"_FirebugCommandLine":{},}

try {
  JSON.stringify(alert);
} catch (err) {
  alert(err); // Error: Invalid argument.
}

try {
  JSON.stringify("aiueo");
} catch (err) {
  alert(err); // Error: Invalid argument.
}

try {
  JSON.stringify(1);
} catch (err) {
  alert(err); // Error: Invalid argument.
}

JSON.stringify(document.links);
/*
{"0":{"scrollWidth":26,"clientLeft":0,"clientHeight":0,"clientWidth":0,"clientTop":0,},"1":{"scrollWidth":26,"clientLeft":0,"clientHeight":0,"clientWidth":0,"clientTop":0,},"2":
以下省略
*/

JSON.stringify([1, 2, 3]); // [1,2,3]

配列風オブジェクトは、配列ではなく普通のオブジェクトとして文字列にされる。オブジェクトが入れ子になってても再帰的に辿られる。
配列は、普通に配列としてパースできる。
windowなどのオブジェクトを渡すと、一部の列挙可能なプロパティのみ文字列にされる。
関数やメソッド、文字列、数値のみを渡した場合はパースエラーになる。これはJSONの仕様だと思う。

追記:

json2.jsと同じように、Date.prototype.toJSONなどが定義されていて、オブジェクトにtoJSONメソッドを定義することで、JSON.stringifyを通した場合の実行結果を調整することができる。

例えば、

function FillAll(str, n) {
  for (var i = 0; i < n; ++i)
    this[i] = str;
  this.length = n;
}

FillAll.prototype.toJSON = function toJSON() {
  return Array.join(this, "+");
};

JSON.stringify({
  foo: new FillAll("*", 10)
}); // {"foo":"*+*+*+*+*+*+*+*+*+*"}

のようにできる。

参考:408838 – DOM binding for native JSON