將javascript object轉成JSON格式字串

JSON.stringify()可以產出一個object的JSON格式字串,此處將JSON.stringify()更進一步enhance,以避免object內有無窮循環連結的問題。

並不是每個瀏覽器的javascript都已經內建了JSON物件,為此stackoverflow上有人提供了JSON.stringify()程式碼,以解決這個問題。但該程式碼會有個問題:給如下的object a

a=[]
b={}
b.f1="good"
b.f2="nice"
b.parent= a
a.push(b)
a.push(a)

這個object a有個特殊情況是,a[0].parent會指回自己,a[1]也是指回自己。把這種含有無窮循環連結的物件送入傳統的JSON.stringify()後,將會導致無窮迴圈,因而程式產生錯誤。以下的增強版本會跳過「已處理過」的子物件,因而避掉前述的問題

// implement JSON.stringify serialization 
// original source: http://stackoverflow.com/questions/4715373/json-object-undefined-in-internet-explorer-8-dom
var JSON = JSON || {};
JSON.stringify= function (obj,par) {
	if (typeof par=='undefined' || par=='debug'){	// to initialize it
		JSON.ii=0;		// stringify iteration index
		JSON.obj=[];	// array containing the object that had been processed by stringify
		JSON.obj.push(obj);
		JSON.debug= (par=='debug' && typeof par!='undefined' )
		}
	var t = typeof (obj);
	if (t != "object" || obj === null) {
		// simple data type
		if (t == "string") obj = '"'+ obj.replace(/\\/ig,"\\\\").replace(/"/ig,'\\"').replace(/\n/ig,"\\n").replace(/\t/ig,"\\t") +'"';
		return String(obj);
		}
	else {
		// recurse array or object
		var n, v, json = [];		
		var arr = (obj && Array.isArray(obj));	//seems better than arr = (obj && obj.constructor == Array);		

		for (n in obj) {
			v = obj[n]; 
			t = typeof(v);
			if (t == "function")  continue; 		// skip it, if the object is a function
			
			if (t == "string") v = '"'+v.replace(/\\/ig,"\\\\").replace(/"/ig,'\\"').replace(/\n/ig,"\\n").replace(/\t/ig,"\\t") +'"';
			else if (t == "object" && v !== null) {
				if (JSON.obj.includes(v)){
					if (JSON.debug){
						try{
						console.log("!!-recursive object detected and omitted: " + (arr? "array[" + n + "]": "object '" + n + "'"))
						} catch(err){}
						}
					continue;		// skip it, if the object had been processed
					}
				JSON.obj.push(v);
				JSON.ii++
				//if (JSON.ii++>100) return		// turn on it, if you want to prevent it from infinite iteration
				if (JSON.debug){
					try{
					if (arr)
						console.log("[level:" + JSON.ii + "] array[" + n + "]")
					else
						console.log("[level:" + JSON.ii + " object]" + n)
					} catch(err){}
					}
				v = JSON.stringify(v,"sub_object");
				JSON.ii--
				}

			json.push((arr ? "" : '"' + n + '":') + String(v));
			if (JSON.debug){
				try{
				console.log((arr ? "" : '"' + n + '":') + String(v))
				} catch(err){}
				}
			}	// for

		return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
		}
	}

測試結果如下(這是在Chrome執行的結果)

JSON.stringify(a)
"[{"f1":"good","f2":"nice"}]"

JSON.stringify(b)
"{"f1":"good","f2":"nice","parent":[]}"

a[0].parent指回自己,a[1]也是指回自己,所以都被跳過,而不會出現在JSON.stringify(a)的結果。
同樣地,b.parent[0]與b重複、b.parent[1]與b.parent重複,所以b.parent[0]和b.parent[1]都會被跳到,而使得b.parent在JSON.stringify(b)的結果中變成是empty array的呈現。

另外,此增強版本的JSON.stringify()還有一個debug功能,第二個參數設為"debug",就可以將stringify運作過程的一些資料顯示出來 (這是在Chrome執行的結果)

JSON.stringify(a,"debug")
VM501:43 [level:1] array[0]
VM501:55 "f1":"good"
VM501:55 "f2":"nice"
VM501:32 !!-recursive object detected and omitted: object 'parent'
VM501:55 {"f1":"good","f2":"nice"}
VM501:32 !!-recursive object detected and omitted: array[1]
"[{"f1":"good","f2":"nice"}]"