上記の問題を解消する方法を思いつき、サンプルを作成したので公開します。
まず、旧verのJSON化関数に「参照が重複しているプロパティを持つオブジェクト」を与えると以下のようになってしまいます。
※元々はobj.aとobj.bの中身は同じ配列だったので、例えば「obj.a.push(1)」を実行するとobj.bから参照している配列でも要素が増えるが、旧verのJSONで再現したものではobj.aとobj.bのそれぞれが別の配列を参照しているので、片方にpushしてももう片方には影響しない。再現できていない。
本日公開するJSON化関数では、元のオブジェクトの構造を正しく再現できるため、以下のようになります。
最新のJSON化関数のソースは以下。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
LEN='length' | |
for0L = function(arr,fun){for(var i=0,L=arr[LEN],res;i<L;i++){if(res=fun(i,arr[i])){return res}}} | |
forIn = function(obj,fun){var name,res; for(name in obj){if(res=fun(name, obj[name])){return res}}} | |
インデント調整=function(f){ var s=f+'', 最終行=s.match(/([\t ]*)\}$/)[1]; return s.replace(new RegExp('\\n'+最終行,'g'),'\n') } | |
toJSON=function(){ | |
var gt=function(o){return typeof(o)}, gc=function(o){return o.constructor}, objT={}, objC={}, fStr, rNg=/\n/g, nt='\n\t', main | |
objT[gt('')] = fStr = function(s){return '"'+s.replace(/\\/g,'\\\\').replace(/\r/g,'\\r').replace(/\n/g,'\\n').replace(/"/g,'\\"')+'"'} | |
objT[gt(0)] = objT[gt(true)] = objT[gt(gt.und)] = objC[gc(/1/)] = function(v){return v} | |
objC[gc(new Date())] = function(o){return 'new Date("'+o+'")'} | |
objC[gc(gc)] = function(o){return インデント調整(o)} | |
objC[gc({})] = function(o){ | |
if(循環.名前push(o)){return '"参照の重複"'} | |
var a=[], i=0 | |
forIn(o,function(n,v){ a[i++] = fStr(循環.名前=n)+':'+(main(v)+'').replace(rNg,nt) }) | |
循環.pop() | |
return '{'+nt+a.join(','+nt)+'\n}' | |
} | |
objC[gc([])] = function(o){ | |
if(循環.名前push(o)){return '"参照の重複"'} | |
var a0=[], s, a1=[], i=0 | |
for0L(o,function(i,v){a0[循環.名前=i]=(main(v)+'').replace(rNg,nt)}), s='['+nt+a0.join(','+nt)+'\n]' | |
forIn(o,function(n,v){ !a0[n] && (a1[i++] = 'a['+fStr(循環.名前=n)+']='+main(v)) }) | |
循環.pop() | |
return a1[LEN] ? (インデント調整(function(){ | |
var a=sss | |
aaa111 | |
return a | |
})+'()').replace(/sss/,s.replace(rNg,nt)).replace(/aaa111/,a1.join(nt)) : s | |
} | |
var 循環=function(){ | |
var arr候補, arr確定, key, arr重複候補 | |
var getKey=function(){return key.length ? '['+key.join('][')+']' : ''} | |
return { | |
init:function(){arr候補=[]; arr確定=[]; key=[]; arr重複候補=[], this.名前=''}, | |
名前:'', | |
名前push:function(o){ | |
this.名前 && key.push(fStr(this.名前)) | |
// 追加した名前に対応するoと一致するものがarr候補にあるならtrueを返す | |
var fun=function(i,候補){return o==候補.o && arr確定.push({s:候補.key, d:getKey()}) && key.pop()} | |
if(key.length && (forIn(arr候補,fun) || forIn(arr重複候補,fun))){return true} | |
// 一致するものがなければ候補に追加する | |
arr候補.push({o:o, key:getKey()}) | |
}, | |
pop:function(){key.pop(), arr重複候補.push(arr候補.pop())}, | |
getRes:function(objName){var a=[]; for0L(arr確定,function(i,o){ a.push(objName+o.d+' = '+objName+o.s) }); return a} | |
} | |
}() | |
main=function(o){ var t=gt(o); return o===null ? 'null' : objT[t] ? objT[t](o) : objC[gc(o)](o) } | |
return function(o){ | |
循環.init() | |
var s=main(o), a=循環.getRes('o') | |
return !a.length ? s : 'function(){\n\tvar o='+s.replace(rNg,nt)+'\n\t'+a.join('\n\t')+'\n\treturn o\n}()' | |
} | |
}() | |
obj = { | |
a:[], | |
b:{ | |
c:2, | |
e:{} | |
} | |
} | |
obj.c = obj | |
obj.b.d = obj.b | |
obj.b.e.a = obj.a | |
var str=toJSON(obj), obj0 | |
eval('obj0 = '+str) | |
obj0.b.e.a.push(123) | |
/* | |
function(){ | |
var o={ | |
"a":[ | |
], | |
"b":{ | |
"c":2 | |
"e":{ | |
"a":"参照の重複" | |
}, | |
"d":"参照の重複" | |
}, | |
"c":"参照の重複" | |
} | |
o["b"]["e"]["a"] = o["a"] | |
o["b"]["d"] = o["b"] | |
o["c"] = o | |
return o | |
}() | |
function(){ | |
var o={ | |
"a":[ | |
123 | |
], | |
"b":{ | |
"c":2 | |
"e":{ | |
"a":"参照の重複" | |
}, | |
"d":"参照の重複" | |
}, | |
"c":"参照の重複" | |
} | |
o["b"]["e"]["a"] = o["a"] | |
o["b"]["d"] = o["b"] | |
o["c"] = o | |
return o | |
}() | |
*/ | |
WScript.Echo(str+'\n\n'+toJSON(obj0)) |
0 件のコメント:
コメントを投稿