2012-01-08 113 views
27

當使用JSON.stringify()時,json2.js似乎忽略父對象的成員。例如:如何將繼承的對象字符串化爲JSON?

require('./json2.js'); 

function WorldObject(type) {  
    this.position = 4; 
} 

function Actor(val) { 
    this.someVal = 50; 
} 

Actor.prototype = new WorldObject(); 

var a = new Actor(2); 

console.log(a.position); 
console.log(JSON.stringify(a)); 

輸出是:

4 
{"someVal":50} 

我希望這樣的輸出:

4 
{"position":0, "someVal":50} 

回答

31

嗯,這只是事情是這樣的,JSON.stringify不保留任何不可─的擁有該對象的屬性。您可以查看關於其他缺點和可能的解決方法here的有趣討論。

另請注意,作者不僅記錄了問題,而且還編寫了一個名爲HydrateJS的庫,可能會對您有所幫助。

這個問題比第一眼看起來要深一點。即使a真的會變成{"position":0, "someVal":50},然後解析它將創建一個具有所需屬性的對象,但既不是Actor的實例,也不是它的WorldObject的原型鏈接(畢竟,解析方法不有這個信息,所以它不可能以這種方式恢復)。

爲了保留原型鏈,聰明的技巧是必要的(如HydrateJS中使用的那些技巧)。如果這不是你想要的,也許你只需要在對其進行串化之前將對象「變平」。要做到這一點,你可以迭代對象的所有屬性,而不管它們是否屬於它們,並重新賦值它們(這將確保它們在對象本身上定義,而不僅僅是從原型繼承)。

function flatten(obj) { 
    var result = Object.create(obj); 
    for(var key in result) { 
     result[key] = result[key]; 
    } 
    return result; 
} 

函數寫入的方式不改變原始對象。因此,使用

console.log(JSON.stringify(flatten(a))); 

你會得到你想要的輸出和a將保持不變。

+0

我認爲這段代碼基本上是我需要的。我應該能夠保存這個序列化版本並編寫一個簡單的加載器。幹得不錯! – wtjones 2012-01-08 20:14:45

9

檢查此琴:

function flatStringify(x) { 
    for(var i in x) { 
     if(!x.hasOwnProperty(i)) { 
      // weird as it might seem, this actually does the trick! - adds parent property to self 
      x[i] = x[i]; 
     } 
    } 
    return JSON.stringify(x); 
} 
+0

我希望我可以將兩者都標記爲答案,因爲這同樣適用於Tomas的片段,謝謝! – wtjones 2012-01-08 20:12:09

20

另一種選擇是定義在對象原型toJSON方法要序列:http://jsfiddle.net/AEGYG/

使用此功能,您可以平字符串化的對象

function Test(){} 

Test.prototype = { 

    someProperty: "some value", 

    toJSON: function() { 
     var tmp = {}; 

     for(var key in this) { 
      if(typeof this[key] !== 'function') 
       tmp[key] = this[key]; 
     } 

     return tmp; 
    } 
}; 

var t = new Test; 

JSON.stringify(t); // returns "{"someProperty" : "some value"}" 

這是可行的,因爲JSON.stringify在它接收的對象中搜索toJSON方法,然後嘗試na tive序列化。

1

雖然flatten方法在一般情況下可行,但截至目前爲止發佈的其他答案中的片段不適用於不可修改的屬性,例如,如果原型已爲frozen。要處理這種情況,您需要創建一個新對象並將屬性分配給這個新對象。既然你只是把結果對象串起來,對象標識和其他JavaScript內部可能並不重要,所以返回一個新對象是完全正確的。這種方法也可以說是比重新分配一個對象的屬性本身更具可讀性,因爲它看起來並不像一個無操作:

function flatten(obj) { 
    var ret = {}; 
    for (var i in obj) { 
     ret[i] = obj[i]; 
    } 
    return ret; 
} 
1

以下是摘錄@TomasVana的遞歸版本包含在他的回答中,如果在您的對象樹的多個級別中存在繼承:

var flatten = function(obj) { 
    if (obj === null) { 
     return null; 
    } 

    if (Array.isArray(obj)) { 
     var newObj = []; 
     for (var i = 0; i < obj.length; i++) { 
      if (typeof obj[i] === 'object') { 
       newObj.push(flatten(obj[i])); 
      } 
      else { 
       newObj.push(obj[i]); 
      } 
     } 
     return newObj; 
    } 

    var result = Object.create(obj); 
    for(var key in result) { 
     if (typeof result[key] === 'object') { 
      result[key] = flatten(result[key]); 
     } 
     else { 
      result[key] = result[key]; 
     } 
    } 
    return result; 
} 

並且它將數組保存爲數組。以同樣的方式調用它:

console.log(JSON.stringify(flatten(visualDataViews))); 
相關問題