javascript 如何将继承的对象字符串化为 JSON?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8779249/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to stringify inherited objects to JSON?
提问by wtjones
json2.js seems to ignore members of the parent object when using JSON.stringify(). Example:
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));
The output is:
输出是:
4
{"someVal":50}
I would expect this output:
我希望这个输出:
4
{"position":0, "someVal":50}
回答by Tomas Vana
Well that's just the way it is, JSON.stringify
does not preserve any of the not-owned properties of the object. You can have a look at an interesting discussion about other drawbacks and possible workarounds here.
嗯,这就是它的方式,JSON.stringify
不会保留对象的任何非拥有属性。您可以在此处查看有关其他缺点和可能的解决方法的有趣讨论。
Also note that the author has not only documented the problems, but also written a library called HydrateJSthat might help you.
另请注意,作者不仅记录了问题,还编写了一个名为HydrateJS的库,可能会对您有所帮助。
The problem is a little bit deeper than it seems at the first sight. Even if a
would really stringify to {"position":0, "someVal":50}
, then parsing it later would create an object that has the desired properties, but is neither an instance of Actor, nor has it a prototype link to the WorldObject (after all, the parse method doesn't have this info, so it can't possibly restore it that way).
这个问题比乍一看要深一些。即使真的a
将字符串化为{"position":0, "someVal":50}
,稍后解析它也会创建一个具有所需属性的对象,但它既不是 Actor 的实例,也没有指向 WorldObject 的原型链接(毕竟 parse 方法没有这个信息,因此它不可能以这种方式恢复它)。
To preserve the prototype chain, clever tricks are necessary (like those used in HydrateJS). If this is not what you are aiming for, maybe you just need to "flatten" the object before stringifying it. To do that, you could e.g. iterate all the properties of the object, regardless of whether they are own or not and re-assign them (this will ensure they get defined on the object itself instead of just inherited from the prototype).
为了保留原型链,需要一些巧妙的技巧(就像在 HydrateJS 中使用的那些技巧)。如果这不是您的目标,也许您只需要在字符串化之前“展平”对象。为此,您可以例如迭代对象的所有属性,无论它们是否拥有并重新分配它们(这将确保它们在对象本身上定义,而不是仅从原型继承)。
function flatten(obj) {
var result = Object.create(obj);
for(var key in result) {
result[key] = result[key];
}
return result;
}
The way the function is written it doesn't mutate the original object. So using
函数的编写方式不会改变原始对象。所以使用
console.log(JSON.stringify(flatten(a)));
you'll get the output you want and a
will stay the same.
你会得到你想要的输出并且a
会保持不变。
回答by Cesar Varela
Another option would be to define a toJSON
method in the object prototype you want to serialize:
另一种选择是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"}"
This works since JSON.stringify searches for a toJSON
method in the object it receives, before trying the native serialization.
这是有效的,因为 JSON.stringifytoJSON
在尝试本机序列化之前在它接收的对象中搜索方法。
回答by techfoobar
Check this fiddle: http://jsfiddle.net/AEGYG/
检查这个小提琴:http: //jsfiddle.net/AEGYG/
You can flat-stringify the object using this function:
您可以使用此函数对对象进行扁平化字符串化:
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);
}
回答by kiewic
Here is a recursive version of the snippet @TomasVana included in his answer, in case there is inheritance in multiple levels of your object tree:
这是他的答案中包含的片段@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;
}
And it keeps arrays as arrays. Call it the same way:
并将数组保留为数组。以同样的方式调用它:
console.log(JSON.stringify(flatten(visualDataViews)));
回答by wlritchi
While the flatten
approach in general works, the snippets in other answers posted so far don't work for properties that are not modifiable, for example if the prototype has been frozen. To handle this case, you would need to create a new object and assign the properties to this new object. Since you're just stringifying the resulting object, object identity and other JavaScript internals probably don't matter, so it's perfectly fine to return a new object. This approach is also arguably more readable than reassigning an object's properties to itself, since it doesn't look like a no-op:
虽然该flatten
方法通常有效,但迄今为止发布的其他答案中的片段不适用于不可修改的属性,例如,如果原型已被冻结。要处理这种情况,您需要创建一个新对象并将属性分配给这个新对象。由于您只是对结果对象进行字符串化,因此对象标识和其他 JavaScript 内部结构可能无关紧要,因此返回一个新对象完全没问题。这种方法也可以说比将对象的属性重新分配给自身更具可读性,因为它看起来不像一个空操作:
function flatten(obj) {
var ret = {};
for (var i in obj) {
ret[i] = obj[i];
}
return ret;
}
回答by Marc W?ckerlin
JSON.stringify
takes three options
JSON.stringify(value[, replacer[, space]])
So, make use of the replacer
, which is a function, that is called recursively for every key
-value
-pair.
因此,使用的replacer
,这是一个功能,那就是递归调用每一个key
- value
-pair。
Next Problem, to get really everything, you need to follow the prototpesand you must use getOwnPropertyNames
to get all property names (more than you can catch with keys
or for…in
):
下一个问题,要获得真正的所有内容,您需要遵循原型,并且必须使用getOwnPropertyNames
来获取所有属性名称(比您可以使用keys
或捕获的更多for…in
):
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;
};
};
Then flatten the object to JSON:
然后将对象展平为 JSON:
JSON.stringify(myValue, flatten());
Notes:
笔记:
- I had a case where value was
null
, buttypeof value
was"object"
- Circular referencesmust bee detected, so it needs
seen
- 我曾在那里值的情况下
null
,却typeof value
是"object"
- 必须检测循环引用,因此需要
seen