在 JavaScript 中序列化/反序列化对象的最佳方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6487699/
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
Best way to serialize/unserialize objects in JavaScript?
提问by Topera
I have many JavaScript objects in my application, something like:
我的应用程序中有许多 JavaScript 对象,例如:
function Person(age) {
this.age = age;
this.isOld = function (){
return this.age > 60;
}
}
// before serialize, ok
var p1 = new Person(77);
alert("Is old: " + p1.isOld());
// after, got error Object #<Object> has no method 'isOld'
var serialize = JSON.stringify(p1);
var _p1 = JSON.parse(serialize);
alert("Is old: " + _p1.isOld());
See in JS Fiddle.
请参阅JS Fiddle。
My question is: is there a best practice/pattern/tip to recover my object in same type it was before serialization (instances of class Person, in this case)?
我的问题是:是否有最佳实践/模式/技巧来恢复我的对象在序列化之前的相同类型(在这种情况下是 Person 类的实例)?
Requirements that I have:
我的要求:
- Optimize disk usage: I have a big tree of objects in memory. So, I don't want to store functions.
- Solution can use jQuery and another library to serialize/unserialize.
- 优化磁盘使用:我在内存中有一个很大的对象树。所以,我不想存储功能。
- 解决方案可以使用 jQuery 和另一个库来序列化/反序列化。
回答by Felix Kling
JSON has no functions as data types. You can only serialize strings, numbers, objects, arrays, and booleans (and null
)
JSON 没有作为数据类型的功能。您只能序列化字符串、数字、对象、数组和布尔值(和null
)
You could create your own toJson
method, only passing the data that really has to be serialized:
您可以创建自己的toJson
方法,只传递真正需要序列化的数据:
Person.prototype.toJson = function() {
return JSON.stringify({age: this.age});
};
Similar for deserializing:
类似于反序列化:
Person.fromJson = function(json) {
var data = JSON.parse(json); // Parsing the json string.
return new Person(data.age);
};
The usage would be:
用法是:
var serialize = p1.toJson();
var _p1 = Person.fromJson(serialize);
alert("Is old: " + _p1.isOld());
To reduce the amount of work, you could consider to store all the data that needs to be serialized in a special "data" property for each Person
instance. For example:
为了减少工作量,您可以考虑将所有需要序列化的数据存储在每个Person
实例的特殊“数据”属性中。例如:
function Person(age) {
this.data = {
age: age
};
this.isOld = function (){
return this.data.age > 60 ? true : false;
}
}
then serializing and deserializing is merely calling JSON.stringify(this.data)
and setting the data of an instance would be instance.data = JSON.parse(json)
.
那么序列化和反序列化只是调用JSON.stringify(this.data)
和设置实例的数据instance.data = JSON.parse(json)
。
This would keep the toJson
and fromJson
methods simple but you'd have to adjust your other functions.
这将使toJson
和fromJson
方法保持简单,但您必须调整其他功能。
Side note:
边注:
You should add the isOld
method to the prototype of the function:
您应该将该isOld
方法添加到函数的原型中:
Person.prototype.isOld = function() {}
Otherwise, every instance has it's own instance of that function which also increases memory.
否则,每个实例都有它自己的该函数实例,这也会增加内存。
回答by Etienne
I wrote serialijsebecause I faced the same problem as you.
我写serialijse是因为我遇到了和你一样的问题。
you can find it at https://github.com/erossignon/serialijse
你可以在https://github.com/erossignon/serialijse找到它
It can be used in nodejs or in a browser and can serve to serialize and deserialize a complex set of objects from one context (nodejs) to the other (browser) or vice-versa.
它可以在 nodejs 或浏览器中使用,并且可以用于将一组复杂的对象从一个上下文(nodejs)序列化和反序列化到另一个(浏览器),反之亦然。
var s = require("serialijse");
var assert = require("assert");
// testing serialization of a simple javascript object with date
function testing_javascript_serialization_object_with_date() {
var o = {
date: new Date(),
name: "foo"
};
console.log(o.name, o.date.toISOString());
// JSON will fail as JSON doesn't preserve dates
try {
var jstr = JSON.stringify(o);
var jo = JSON.parse(jstr);
console.log(jo.name, jo.date.toISOString());
} catch (err) {
console.log(" JSON has failed to preserve Date during stringify/parse ");
console.log(" and has generated the following error message", err.message);
}
console.log("");
var str = s.serialize(o);
var so = s.deserialize(str);
console.log(" However Serialijse knows how to preserve date during serialization/deserialization :");
console.log(so.name, so.date.toISOString());
console.log("");
}
testing_javascript_serialization_object_with_date();
// serializing a instance of a class
function testing_javascript_serialization_instance_of_a_class() {
function Person() {
this.firstName = "Joe";
this.lastName = "Doe";
this.age = 42;
}
Person.prototype.fullName = function () {
return this.firstName + " " + this.lastName;
};
// testing serialization using JSON.stringify/JSON.parse
var o = new Person();
console.log(o.fullName(), " age=", o.age);
try {
var jstr = JSON.stringify(o);
var jo = JSON.parse(jstr);
console.log(jo.fullName(), " age=", jo.age);
} catch (err) {
console.log(" JSON has failed to preserve the object class ");
console.log(" and has generated the following error message", err.message);
}
console.log("");
// now testing serialization using serialijse serialize/deserialize
s.declarePersistable(Person);
var str = s.serialize(o);
var so = s.deserialize(str);
console.log(" However Serialijse knows how to preserve object classes serialization/deserialization :");
console.log(so.fullName(), " age=", so.age);
}
testing_javascript_serialization_instance_of_a_class();
// serializing an object with cyclic dependencies
function testing_javascript_serialization_objects_with_cyclic_dependencies() {
var Mary = { name: "Mary", friends: [] };
var Bob = { name: "Bob", friends: [] };
Mary.friends.push(Bob);
Bob.friends.push(Mary);
var group = [ Mary, Bob];
console.log(group);
// testing serialization using JSON.stringify/JSON.parse
try {
var jstr = JSON.stringify(group);
var jo = JSON.parse(jstr);
console.log(jo);
} catch (err) {
console.log(" JSON has failed to manage object with cyclic deps");
console.log(" and has generated the following error message", err.message);
}
// now testing serialization using serialijse serialize/deserialize
var str = s.serialize(group);
var so = s.deserialize(str);
console.log(" However Serialijse knows to manage object with cyclic deps !");
console.log(so);
assert(so[0].friends[0] == so[1]); // Mary's friend is Bob
}
testing_javascript_serialization_objects_with_cyclic_dependencies();
回答by T. Webster
The browser's native JSON API may not give you back your idOld function after you call JSON.stringify, however, if can stringify your JSON yourself (maybe use Crockford's json2.jsinstead of browser's API), then if you have a string of JSON e.g.
在您调用 JSON.stringify 之后,浏览器的原生 JSON API 可能不会将您的 idOld 函数返回给您,但是,如果可以自己对您的 JSON 进行字符串化(也许使用Crockford 的 json2.js而不是浏览器的 API),那么如果您有一个 JSON 字符串,例如
var person_json = "{ \"age:\" : 20, \"isOld:\": false, isOld: function() { return this.age > 60; } }";
then you can call
然后你可以打电话
eval("(" + person + ")")
, and you will get back your function in the json object.
,您将在 json 对象中取回您的函数。
回答by Joon
I am the author of https://github.com/joonhocho/seri.
我是https://github.com/joonhocho/seri的作者。
Seri is JSON + custom (nested) class support.
Seri 是 JSON + 自定义(嵌套)类支持。
You simply need to provide toJSON
and fromJSON
to serialize and deserialize any class instances.
您只需要提供toJSON
并fromJSON
序列化和反序列化任何类实例。
Here's an example with nested class objects:
这是一个嵌套类对象的示例:
import seri from 'seri';
class Item {
static fromJSON = (name) => new Item(name)
constructor(name) {
this.name = name;
}
toJSON() {
return this.name;
}
}
class Bag {
static fromJSON = (itemsJson) => new Bag(seri.parse(itemsJson))
constructor(items) {
this.items = items;
}
toJSON() {
return seri.stringify(this.items);
}
}
// register classes
seri.addClass(Item);
seri.addClass(Bag);
const bag = new Bag([
new Item('apple'),
new Item('orange'),
]);
const bagClone = seri.parse(seri.stringify(bag));
// validate
bagClone instanceof Bag;
bagClone.items[0] instanceof Item;
bagClone.items[0].name === 'apple';
bagClone.items[1] instanceof Item;
bagClone.items[1].name === 'orange';
Hope it helps address your problem.
希望它有助于解决您的问题。
回答by fishgen
I had the exact same problem, and written a small tool to do the mixing of data and model. See https://github.com/khayll/jsmix
我遇到了完全相同的问题,并编写了一个小工具来混合数据和模型。见https://github.com/khayll/jsmix
This is how you would do it:
这是你会怎么做:
//model object (or whatever you'd like the implementation to be)
var Person = function() {}
Person.prototype.isOld = function() {
return this.age > RETIREMENT_AGE;
}
//then you could say:
var result = JSMix(jsonData).withObject(Person.prototype, "persons").build();
//and use
console.log(result.persons[3].isOld());
It can handle complex objects, like nested collections recursively as well.
它可以处理复杂的对象,例如递归嵌套集合。
As for serializing JS functions, I wouldn't do such thing because of security reasons.
至于序列化JS函数,出于安全考虑,我不会这样做。
回答by Wavey
I had a similar problem and since I couldn't find a sufficient solution, I also created a serialization library for javascript: https://github.com/wavesoft/jbb(as a matter of fact it's a bit more, since it's mainly intended for bundling resources)
我遇到了类似的问题,由于找不到足够的解决方案,我还为 javascript 创建了一个序列化库:https: //github.com/wavesoft/jbb(实际上它有点多,因为它主要是用于捆绑资源)
It is close to Binary-JSON but it adds a couple of additional features, such as metadata for the objects being encoded and some extra optimizations like data de-duplication, cross-referencing to other bundles and structure-level compression.
它与 Binary-JSON 很接近,但它增加了一些额外的功能,比如被编码对象的元数据和一些额外的优化,比如重复数据删除、与其他包的交叉引用和结构级压缩。
However there is a catch: In order to keep the bundle size small there are no type information in the bundle. Such information are provided in a separate "profile" that describes your objects for encoding and decoding. For optimization reasons this information is given in a form of script.
但是有一个问题:为了保持包的大小较小,包中没有类型信息。此类信息在单独的“配置文件”中提供,该配置文件描述了用于编码和解码的对象。出于优化原因,此信息以脚本的形式给出。
But you can make your life easier using the gulp-jbb-profile
(https://github.com/wavesoft/gulp-jbb-profile) utility for generating the encodeing/decoding scripts from simple YAML object specifications like this:
但是,您可以使用gulp-jbb-profile
( https://github.com/wavesoft/gulp-jbb-profile) 实用程序从简单的 YAML 对象规范中生成编码/解码脚本,如下所示:
# The 'Person' object has the 'age' and 'isOld'
# properties
Person:
properties:
- age
- isOld
For example you can have a look on the jbb-profile-three
profile.
When you have your profile ready, you can use JBB like this:
例如,您可以查看jbb-profile-three
个人资料。准备好个人资料后,您可以像这样使用 JBB:
var JBBEncoder = require('jbb/encode');
var MyEncodeProfile = require('profile/profile-encode');
// Create a new bundle
var bundle = new JBBEncoder( 'path/to/bundle.jbb' );
// Add one or more profile(s) in order for JBB
// to understand your custom objects
bundle.addProfile(MyEncodeProfile);
// Encode your object(s) - They can be any valid
// javascript object, or objects described in
// the profiles you added previously.
var p1 = new Person(77);
bundle.encode( p1, 'person' );
var people = [
new Person(45),
new Person(77),
...
];
bundle.encode( people, 'people' );
// Close the bundle when you are done
bundle.close();
And you can read it back like this:
你可以像这样读回它:
var JBBDecoder = require('jbb/decode');
var MyDecodeProfile = require('profile/profile-decode');
// Instantiate a new binary decoder
var binaryLoader = new JBBDecoder( 'path/to/bundle' );
// Add your decoding profile
binaryLoader.addProfile( MyDecodeProfile );
// Add one or more bundles to load
binaryLoader.add( 'bundle.jbb' );
// Load and callback when ready
binaryLoader.load(function( error, database ) {
// Your objects are in the database
// and ready to use!
var people = database['people'];
});
回答by Nico Westerdale
I've added yet another JavaScript serializer repo to GitHub.
我已经向 GitHub 添加了另一个 JavaScript 序列化程序存储库。
Rather than take the approach of serializing and deserializing JavaScript objects to an internal format the approach here is to serialize JavaScript objects out to native JavaScript. This has the advantage that the format is totally agnostic from the serializer, and the object can be recreated simply by calling eval().
这里的方法不是采用将 JavaScript 对象序列化和反序列化为内部格式的方法,而是将 JavaScript 对象序列化为原生 JavaScript。这样做的优点是格式与序列化程序完全无关,并且可以通过调用 eval() 简单地重新创建对象。
回答by Polv
I tried to do this with Date
with native JSON
...
我试图Date
用本机来做到这一点JSON
......
function stringify (obj: any) {
return JSON.stringify(
obj,
function (k, v) {
if (this[k] instanceof Date) {
return ['$date', +this[k]]
}
return v
}
)
}
function clone<T> (obj: T): T {
return JSON.parse(
stringify(obj),
(_, v) => (Array.isArray(v) && v[0] === '$date') ? new Date(v[1]) : v
)
}
What does this say? It says
这说明了什么?它说
- There needs to be a unique identifier, better than
$date
, if you want it more secure.
$date
如果您希望它更安全,则需要一个唯一标识符,比 更好。
class Klass {
static fromRepr (repr: string): Klass {
return new Klass(...)
}
static guid = '__Klass__'
__repr__ (): string {
return '...'
}
}
This is a serializable Klass, with
这是一个可序列化的 Klass,具有
function serialize (obj: any) {
return JSON.stringify(
obj,
function (k, v) { return this[k] instanceof Klass ? [Klass.guid, this[k].__repr__()] : v }
)
}
function deserialize (repr: string) {
return JSON.parse(
repr,
(_, v) => (Array.isArray(v) && v[0] === Klass.guid) ? Klass.fromRepr(v[1]) : v
)
}
I tried to do it with Mongo-style Object ({ $date }
) as well, but it failed in JSON.parse
. Supplying k
doesn't matter anymore...
我也尝试用 Mongo 风格的 Object ( { $date }
)来做,但它在JSON.parse
. 供应k
不再重要...
BTW, if you don't care about libraries, you can use yaml.dump
/ yaml.load
from js-yaml
. Just make sure you do it the dangerous way.
顺便说一句,如果你不关心图书馆,你可以使用yaml.dump
/ yaml.load
from js-yaml
。只要确保你以危险的方式去做。