Edited at

JSONもどき(値として関数を持つ)をJSON.stringifyしたりJSON.parseしたりする

More than 5 years have passed since last update.


値として関数を持った「JSONもどき」、使いたい時ってないですか?

皆さん、ご存知の通り、

var json_modoki = 

{
foo:100,
boo:"sample text",
hoge: {
muda: "よろしく"
},
func: function() { console.log("hello world"+this.hoge.muda); },
func2: function() { console.log("hello japan"+this.hoge.muda); }
}

のような、値として関数を持っているようなものはJSONの仕様上、許されていません。

でも、こういう関数を持った「JSONもどき」って、たまに便利なときがありますよね。

ただ、問題なのがこういう「JSONもどき」ではJSON.stringifyによる文字列化や、

文字列化したものをJSON.parseで「JSONもどき」へ戻すことができないということ。

困りましたねー。

ということで、解決策を作ってみました。いや、私が作ったというか、白状しますと、2chで質問したらスーパーハカーな方が基本的なアイデアを教えてくれました。ありがとうございますw


基本方針

基本方針は、「JSONもどき内で値が関数のものは、文字列に変換する」です。

そして、「JSON.parseするときに、独自のパーサーを渡して、パーサー内で関数文字列をevalして関数に戻す」というものです。

詳細はコードを御覧ください。

function convertFunctionsToStringsInJSON(json_with_function) {

var json = json_with_function;

for (key in json) {
if(typeof(json[key]) === "function") {
json[key] = json[key].toString();
}
}

return json;
}

var json =
{
foo:100,
boo:"sample text",
hoge: {
muda: "よろしく"
},
func: function() { console.log("hello world"+this.hoge.muda); },
func2: function() { console.log("hello japan"+this.hoge.muda); }
}

convertFunctionsToStringsInJSON(json);

console.log("stringified:"+JSON.stringify(json));

var jsonText = JSON.stringify(json);

var parser = function(k,v){return v.toString().indexOf('function') === 0 ? eval('('+v+')') : v};
var json2 = JSON.parse(jsonText, parser);

json2.func();
json2.func2();

上記のコードは、jsdo.itでも公開しています。


追記:もっと簡単なやり方

追記:関数の文字列化については、もっと簡単な方法があります。

Function.prototype.toJSON = Function.prototype.toString; とすると、JSON.stringifyの処理での関数の評価時に自動的にtoString()が発動します。

Function.prototype.toJSON = Function.prototype.toString;

var json =
{
foo:100,
boo:"sample text",
hoge: {
muda: "よろしく"
},
func: function() { console.log("hello world"+this.hoge.muda); },
func2: function() { console.log("hello japan"+this.hoge.muda); }
}

console.log("stringified:"+JSON.stringify(json));

var jsonText = JSON.stringify(json);

var parser = function(k,v){return v.toString().indexOf('function') === 0 ? eval('('+v+')') : v};
var json2 = JSON.parse(jsonText, parser);

json2.func();
json2.func2();

こちらもjsdo.itで公開しました。


最後に

いかがでしょうか。もちろん、こんな「JSONもどき」他のサービスとやりとりしたりなどすることはないと思いますが、自アプリ・自サービス内で内部的に使うのはアリだと思います。

そんなに多くないと思いますが、「このTips使えそう」と思ってくれた方がいらっしゃれば幸いです。