javascript 使用方法将 JSON 字符串转换为对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8111446/
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
Turning JSON strings into objects with methods
提问by Cystack
I have an app that allows users to generate objects, and store them (in a MySQL table, as strings) for later use. The object could be :
我有一个应用程序,它允许用户生成对象,并将它们存储(在 MySQL 表中,作为字符串)供以后使用。对象可能是:
function Obj() {
this.label = "new object";
}
Obj.prototype.setLabel = function(newLabel) {
this.label = newLabel;
}
If I use JSON.stringify on this object, I will only get the information on Obj.label
(the stringified object would be a string like {label: "new object"}
. If I store this string, and want to allow my user to retrieve the object later, the setLabel
method will be lost.
如果我在这个对象上使用 JSON.stringify,我只会得到信息Obj.label
(字符串化的对象将是一个像 的字符串{label: "new object"}
。如果我存储这个字符串,并希望我的用户稍后检索该对象,该setLabel
方法将丢失.
So my question is: how can I re-instantiate the object, so that it keeps the properties stored thanks to JSON.stringify, but also gets back the different methods that should belong to its prototype. How would you do that ? I was thinking of something along "create a blank object" and "merge it with the stored one's properties", but I can't get it to work.
所以我的问题是:我怎样才能重新实例化对象,以便它通过 JSON.stringify 保留存储的属性,但也取回应该属于其原型的不同方法。你会怎么做?我正在考虑“创建一个空白对象”和“将其与存储的属性合并”,但我无法让它工作。
采纳答案by T.J. Crowder
To do this, you'll want to use a "reviver" function when parsing the JSON string (and a "replacer" function or a toJSON
function on your constructor's prototype when creating it). See Section 15.12.2and 15.12.3of the specification. If your environment doesn't yet support native JSON parsing, you can use one of Crockford's parsers(Crockford being the inventor of JSON), which also support "reviver" functions.
为此,您需要在解析 JSON 字符串时使用“reviver”函数(以及toJSON
在创建它时使用“replacer”函数或构造函数原型上的函数)。参见规范的15.12.2和15.12.3节。如果您的环境尚不支持原生 JSON 解析,您可以使用Crockford 的解析器之一(Crockford 是 JSON 的发明者),它也支持“reviver”功能。
Here's a simple bespoke example that works with ES5-compliant browsers (or libraries that emulate ES5 behavior) (live copy, run in Chrome or Firefox or similar), but look after the example for a more generalized solution.
这是一个简单的定制示例,适用于符合 ES5 的浏览器(或模拟 ES5 行为的库)(实时复制,在 Chrome 或 Firefox 或类似设备中运行),但请查看示例以获得更通用的解决方案。
// Our constructor function
function Foo(val) {
this.value = val;
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.toJSON = function() {
return "/Foo(" + this.value + ")/";
};
// An object with a property, `foo`, referencing an instance
// created by that constructor function, and another `bar`
// which is just a string
var obj = {
foo: new Foo(42),
bar: "I'm bar"
};
// Use it
display("obj.foo.value = " + obj.foo.value);
display("obj.foo.nifty = " + obj.foo.nifty);
display("obj.bar = " + obj.bar);
// Stringify it with a replacer:
var str = JSON.stringify(obj);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var obj2 = JSON.parse(str, function(key, value) {
if (typeof value === "string" &&
value.substring(0, 5) === "/Foo(" &&
value.substr(-2) == ")/"
) {
return new Foo(value.substring(5, value.length - 2));
}
return value;
});
// Use the result
display("obj2.foo.value = " + obj2.foo.value);
display("obj2.foo.nifty = " + obj2.foo.nifty);
display("obj2.bar = " + obj2.bar);
Note the toJSON
on Foo.prototype
, and the function we pass into JSON.parse
.
注意toJSON
onFoo.prototype
和我们传入的函数JSON.parse
。
The problem there, though, is that the reviver is tightly coupled to the Foo
constructor. Instead, you can adopt a generic framework in your code, where any constructor function can support a fromJSON
(or similar) function, and you can use just one generalized reviver.
然而,这里的问题在于,reviver 与Foo
构造函数紧密耦合。相反,您可以在代码中采用通用框架,其中任何构造函数都可以支持一个fromJSON
(或类似的)函数,并且您可以只使用一个通用的 reviver。
Here's an example of a generalized reviver that looks for a ctor
property and a data
property, and calls ctor.fromJSON
if found, passing in the full value it received (live example):
下面是一个通用的 reviver 的例子,它寻找一个ctor
属性和一个data
属性,ctor.fromJSON
如果找到就调用,传入它收到的完整值(实时示例):
// A generic "smart reviver" function.
// Looks for object values with a `ctor` property and
// a `data` property. If it finds them, and finds a matching
// constructor that has a `fromJSON` property on it, it hands
// off to that `fromJSON` fuunction, passing in the value.
function Reviver(key, value) {
var ctor;
if (typeof value === "object" &&
typeof value.ctor === "string" &&
typeof value.data !== "undefined") {
ctor = Reviver.constructors[value.ctor] || window[value.ctor];
if (typeof ctor === "function" &&
typeof ctor.fromJSON === "function") {
return ctor.fromJSON(value);
}
}
return value;
}
Reviver.constructors = {}; // A list of constructors the smart reviver should know about
To avoid having to repeat common logic in toJSON
and fromJSON
functions, you could have generic versions:
为避免在toJSON
和fromJSON
函数中重复常见逻辑,您可以使用通用版本:
// A generic "toJSON" function that creates the data expected
// by Reviver.
// `ctorName` The name of the constructor to use to revive it
// `obj` The object being serialized
// `keys` (Optional) Array of the properties to serialize,
// if not given then all of the objects "own" properties
// that don't have function values will be serialized.
// (Note: If you list a property in `keys`, it will be serialized
// regardless of whether it's an "own" property.)
// Returns: The structure (which will then be turned into a string
// as part of the JSON.stringify algorithm)
function Generic_toJSON(ctorName, obj, keys) {
var data, index, key;
if (!keys) {
keys = Object.keys(obj); // Only "own" properties are included
}
data = {};
for (index = 0; index < keys.length; ++index) {
key = keys[index];
data[key] = obj[key];
}
return {ctor: ctorName, data: data};
}
// A generic "fromJSON" function for use with Reviver: Just calls the
// constructor function with no arguments, then applies all of the
// key/value pairs from the raw data to the instance. Only useful for
// constructors that can be reasonably called without arguments!
// `ctor` The constructor to call
// `data` The data to apply
// Returns: The object
function Generic_fromJSON(ctor, data) {
var obj, name;
obj = new ctor();
for (name in data) {
obj[name] = data[name];
}
return obj;
}
The advantage here being that you defer to the implementation of a specific "type" (for lack of a better term) for how it serializes and deserializes. So you might have a "type" that just uses the generics:
这里的优点是您可以推迟特定“类型”的实现(因为没有更好的术语)它是如何序列化和反序列化的。所以你可能有一个只使用泛型的“类型”:
// `Foo` is a constructor function that integrates with Reviver
// but doesn't need anything but the generic handling.
function Foo() {
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.spiffy = "I'm the spiffy inherited property.";
Foo.prototype.toJSON = function() {
return Generic_toJSON("Foo", this);
};
Foo.fromJSON = function(value) {
return Generic_fromJSON(Foo, value.data);
};
Reviver.constructors.Foo = Foo;
...or one that, for whatever reason, has to do something more custom:
...或者,无论出于何种原因,必须做一些更自定义的事情:
// `Bar` is a constructor function that integrates with Reviver
// but has its own custom JSON handling for whatever reason.
function Bar(value, count) {
this.value = value;
this.count = count;
}
Bar.prototype.nifty = "I'm the nifty inherited property.";
Bar.prototype.spiffy = "I'm the spiffy inherited property.";
Bar.prototype.toJSON = function() {
// Bar's custom handling *only* serializes the `value` property
// and the `spiffy` or `nifty` props if necessary.
var rv = {
ctor: "Bar",
data: {
value: this.value,
count: this.count
}
};
if (this.hasOwnProperty("nifty")) {
rv.data.nifty = this.nifty;
}
if (this.hasOwnProperty("spiffy")) {
rv.data.spiffy = this.spiffy;
}
return rv;
};
Bar.fromJSON = function(value) {
// Again custom handling, for whatever reason Bar doesn't
// want to serialize/deserialize properties it doesn't know
// about.
var d = value.data;
b = new Bar(d.value, d.count);
if (d.spiffy) {
b.spiffy = d.spiffy;
}
if (d.nifty) {
b.nifty = d.nifty;
}
return b;
};
Reviver.constructors.Bar = Bar;
And here's how we might test that Foo
and Bar
work as expected (live copy):
下面是我们如何测试Foo
并Bar
达到预期效果(现场副本):
// An object with `foo` and `bar` properties:
var before = {
foo: new Foo(),
bar: new Bar("testing", 42)
};
before.foo.custom = "I'm a custom property";
before.foo.nifty = "Updated nifty";
before.bar.custom = "I'm a custom property"; // Won't get serialized!
before.bar.spiffy = "Updated spiffy";
// Use it
display("before.foo.nifty = " + before.foo.nifty);
display("before.foo.spiffy = " + before.foo.spiffy);
display("before.foo.custom = " + before.foo.custom + " (" + typeof before.foo.custom + ")");
display("before.bar.value = " + before.bar.value + " (" + typeof before.bar.value + ")");
display("before.bar.count = " + before.bar.count + " (" + typeof before.bar.count + ")");
display("before.bar.nifty = " + before.bar.nifty);
display("before.bar.spiffy = " + before.bar.spiffy);
display("before.bar.custom = " + before.bar.custom + " (" + typeof before.bar.custom + ")");
// Stringify it with a replacer:
var str = JSON.stringify(before);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var after = JSON.parse(str, Reviver);
// Use the result
display("after.foo.nifty = " + after.foo.nifty);
display("after.foo.spiffy = " + after.foo.spiffy);
display("after.foo.custom = " + after.foo.custom + " (" + typeof after.foo.custom + ")");
display("after.bar.value = " + after.bar.value + " (" + typeof after.bar.value + ")");
display("after.bar.count = " + after.bar.count + " (" + typeof after.bar.count + ")");
display("after.bar.nifty = " + after.bar.nifty);
display("after.bar.spiffy = " + after.bar.spiffy);
display("after.bar.custom = " + after.bar.custom + " (" + typeof after.bar.custom + ")");
display("(Note that after.bar.custom is undefined because <code>Bar</code> specifically leaves it out.)");
回答by pimvdb
You can indeed create an empty instance and then merge the instance with the data. I recommend using a library function for ease of use (like jQuery.extend
).
您确实可以创建一个空实例,然后将实例与数据合并。我建议使用库函数以方便使用(如jQuery.extend
)。
You had some errors though (function ... = function(...)
, and JSON requires keys to be surrounded by "
).
但是function ... = function(...)
,您遇到了一些错误 ( ,并且 JSON 需要用 括起来的键"
)。
var data = '{"label": "new object"}'; // JSON
var inst = new Obj; // empty instance
jQuery.extend(inst, JSON.parse(data)); // merge
Note that merging like this sets properties directly, so if setLabel
is doing some checking stuff, this won't be done this way.
请注意,像这样合并直接设置属性,所以如果setLabel
正在做一些检查,这不会以这种方式完成。
回答by fflorent
If you want to use the setters of Obj :
如果你想使用 Obj 的设置器:
Obj.createFromJSON = function(json){
if(typeof json === "string") // if json is a string
json = JSON.parse(json); // we convert it to an object
var obj = new Obj(), setter; // we declare the object we will return
for(var key in json){ // for all properties
setter = "set"+key[0].toUpperCase()+key.substr(1); // we get the name of the setter for that property (e.g. : key=property => setter=setProperty
// following the OP's comment, we check if the setter exists :
if(setter in obj){
obj[setter](json[key]); // we call the setter
}
else{ // if not, we set it directly
obj[key] = json[key];
}
}
return obj; // we finally return the instance
};
This requires your class to have setters for all its properties. This method is static, so you can use like this :
这要求您的类为其所有属性设置设置器。此方法是静态的,因此您可以像这样使用:
var instance = Obj.createFromJSON({"label":"MyLabel"});
var instance2 = Obj.createFromJSON('{"label":"MyLabel"}');
回答by dnuttle
So far as I know, this means moving away from JSON; you're now customizing it, and so you take on all of the potential headaches that entails. The idea of JSON is to include data only, not code, to avoid all of the security problems that you get when you allow code to be included. Allowing code means that you have to use eval to run that code and eval is evil.
据我所知,这意味着远离 JSON;您现在正在自定义它,因此您承担了所有潜在的麻烦。JSON 的想法是只包含数据,而不是代码,以避免在允许包含代码时遇到的所有安全问题。允许代码意味着您必须使用 eval 来运行该代码,而 eval 是邪恶的。
回答by Abdul Munim
JavaScript is prototype based programming languagewhich is classless language where object orientation achieved by process of cloning existing objects that serve as prototypes.
JavaScript 是基于原型的编程语言,它是一种无类语言,通过克隆作为原型的现有对象的过程来实现面向对象。
Serializing JSON would be considering any methods, for instance if you have an object
序列化 JSON 将考虑任何方法,例如,如果您有一个对象
var x = {
a: 4
getText: function() {
return x.a;
}
};
You will get just { a:4 }
where getText
method is skipped by the serializer.
你将得到的只是{ a:4 }
其中getText
的方法是通过串行跳过。
I ran into this same trouble a year back and I had to maintain a separate helper class for each of my domain object and used $.extend()
it to my deserializedobject when need, just more like having methods to a base class for the domain objects.
一年前我遇到了同样的问题,我不得不为我的每个域对象维护一个单独的辅助类,并在需要时将$.extend()
它用于我的反序列化对象,更像是为域对象的基类提供方法。
回答by Marthinus Engelbrecht
From ECMAScript 6 onwards you can just do:
从 ECMAScript 6 开始,您可以执行以下操作:
Object.assign(new Obj(), JSON.parse(rawJsonString))
Note: You create a new empty object of the defined type first and then override its properties with the parsed JSON. Not the other way around.
注意:您首先创建定义类型的新空对象,然后使用解析的 JSON 覆盖其属性。不是反过来。
The methods define behaviour and contain no variable data. They are "stored" as your code. So you don't actually have to store them in the database.
这些方法定义行为并且不包含可变数据。它们被“存储”为您的代码。因此,您实际上不必将它们存储在数据库中。
回答by pradeek
You would have to write your own stringify method that stores functions as properties by converting them to strings using the toString method.
您必须编写自己的 stringify 方法,通过使用 toString 方法将函数转换为字符串来将函数存储为属性。
回答by j-a
Try to use toString on the method.
尝试在方法上使用 toString 。
Update:
更新:
Iterate over the methods in obj and store them as string, and then instantiate them with new Function.
遍历 obj 中的方法并将它们存储为字符串,然后使用 new Function 实例化它们。
storedFunc = Obj.prototype.setLabel.toString();
Obj2.prototype['setLabel'] = new Function("return (" + storedFunc + ")")();