如何将继承的对象字符串化为JSON
How to stringify inherited objects to JSON?
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}
事实就是这样,JSON.stringify
不保留对象的任何非所有属性。你可以在这里看到一个关于其他缺点和可能的解决方案的有趣讨论。
还要注意,作者不仅记录了这些问题,还编写了一个名为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
将保持不变。
另一个选项是在要序列化的对象原型中定义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
方法。
检查这个小提琴: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);
}
这是他的答案中包含的@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)));
虽然flatten
方法通常有效,但迄今为止发布的其他答案中的片段不适用于不可修改的属性,例如,如果原型已冻结。要处理这种情况,您需要创建一个新对象并将属性分配给这个新对象。由于您只是对生成的对象进行字符串化,因此对象标识和其他JavaScript内部可能并不重要,因此返回一个新对象是完全可以的。这种方法也可以说比将对象的属性重新分配给它自己更可读,因为它看起来不像是一个无操作:
function flatten(obj) {
var ret = {};
for (var i in obj) {
ret[i] = obj[i];
}
return ret;
}
JSON.stringify
采用三个选项
JSON.stringify(value[, replacer[, space]])
所以,使用replacer
,它是一个函数,对于每个key
-value
-对都会递归调用。
下一个问题是,要真正获得所有内容,您需要遵循prototpes,并且必须使用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;
};
};
然后将对象展平为JSON:
JSON.stringify(myValue, flatten());
注:
- 我有一个例子,值是
null
,但typeof value
是"object"
- 必须检测循环引用,因此需要
seen
- 如何使用JSON字符串中的jQuery填充下拉框
- Json字符串可以'当字符串末尾有“'时,t解码;
- 使用JSON文件中的变量(字符串)填充文本区域
- 将JSON转换为typescript中的字符串数组
- jquery从2个json字符串构建一个复选框表单
- RegEx只匹配JSON字符串中最里面的数组
- 使用javascript或angularjs特定过滤器搜索字符串中第一个img标记的json值
- 通过字符串获取JSON对象
- 获取JSON并使用Javascript解析为字符串
- 用javascript修复这个JSON对象字符串最干净的方法是什么
- javascript数组到字符串JSON
- 角度拆分字符串 json
- 跨域 AJAX 调用返回字符串 JSON,而不是 JSON 对象
- 如何在js或jQuery中获取长度字符串json
- 字符串JSON引用名称,而不是被引用的对象
- 子字符串JSON键
- 将字符串JSON转换为多维JavaScript数组
- 字符串json具有空值
- 将html字符串(json表示)转换为实际的javascript对象
- PHP 存储字符串 JSON 与 JavaScript 不同