json2.js在使用JSON.stringify()时似乎忽略了父对象的成员。例如:
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}
答案 0 :(得分:34)
那就是它的方式,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
将保持不变。
答案 1 :(得分:21)
另一种选择是在要序列化的对象原型中定义toJSON
方法:
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
方法。
答案 2 :(得分:9)
检查这个小提琴:http://jsfiddle.net/AEGYG/
您可以使用此功能对对象进行扁平化:
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);
}
答案 3 :(得分:3)
以下是他的答案中包含的片段@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)));
答案 4 :(得分:1)
虽然flatten
方法一般有用,但到目前为止发布的其他答案中的摘要不适用于不可修改的属性,例如原型是frozen。要处理这种情况,您需要创建一个新对象并将属性分配给此新对象。由于您只是对结果对象进行字符串化,因此对象标识和其他JavaScript内部结构可能并不重要,因此返回新对象完全没问题。这种方法也可以说比将对象的属性重新分配给自己更具可读性,因为它看起来不像是无操作:
function flatten(obj) {
var ret = {};
for (var i in obj) {
ret[i] = obj[i];
}
return ret;
}
答案 5 :(得分:0)
JSON.stringify
takes three options
JSON.stringify(value[, replacer[, space]])
因此,请使用replacer
这个函数,它对每个key
-value
对都递归调用。
下一个问题,要真正获得所有内容,您需要follow the prototpes,并且必须使用getOwnPropertyNames
来获取所有属性名称(比起keys
或{{1} }):
for…in
然后将对象展平为JSON:
var getAllPropertyNames = () => {
const seen = new WeakSet();
return (obj) => {
let props = [];
do {
if (seen.has(obj)) return [];
seen.add(obj);
Object.getOwnPropertyNames(obj).forEach((prop) => {
if (props.indexOf(prop) === -1) props.push(prop);
});
} while ((obj = Object.getPrototypeOf(obj)));
return props;
};
};
var flatten = () => {
const seen = new WeakSet();
const getPropertyNames = getAllPropertyNames();
return (key, value) => {
if (value !== null && typeof value === "object") {
if (seen.has(value)) return;
seen.add(value);
let result = {};
getPropertyNames(value).forEach((k) => (result[k] = value[k]));
return result;
}
return value;
};
};
注意:
JSON.stringify(myValue, flatten());
,但是null
是typeof value
"object"