2012-01-08 106 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))); 
相关问题