我的应用程序中有很多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());
我的问题是:是否有最佳实践/模式/提示来恢复序列化之前的相同类型的对象(在这种情况下是类Person的实例)?
我的要求:
答案 0 :(得分:55)
JSON没有数据类型的功能。您只能序列化字符串,数字,对象,数组和布尔值(以及null
)
您可以创建自己的toJson
方法,只传递真正必须序列化的数据:
Person.prototype.toJson = function() {
return JSON.stringify({age: this.age});
};
类似于反序列化:
Person.fromJson = function(json) {
var data = JSON.parse(json); // Parsing the json string.
return new Person(data.age);
};
用法是:
var serialize = p1.toJson();
var _p1 = Person.fromJson(serialize);
alert("Is old: " + _p1.isOld());
为了减少工作量,您可以考虑将每个需要序列化的所有数据存储在每个Person
实例的特殊“数据”属性中。例如:
function Person(age) {
this.data = {
age: age
};
this.isOld = function (){
return this.data.age > 60 ? true : false;
}
}
然后序列化和反序列化只是调用JSON.stringify(this.data)
并且设置实例的数据将是instance.data = JSON.parse(json)
。
这样可以简化toJson
和fromJson
方法,但您必须调整其他功能。
旁注:
您应该将isOld
方法添加到函数的原型中:
Person.prototype.isOld = function() {}
否则,每个实例都有自己的该函数实例,这也增加了内存。
答案 1 :(得分:6)
我写了 serialijse ,因为我遇到了和你一样的问题。
您可以在https://github.com/erossignon/serialijse
找到它它可以在nodejs或浏览器中使用,可用于将一组复杂的对象从一个上下文(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();
答案 2 :(得分:4)
在您调用JSON.stringify之后,浏览器的本机JSON API可能无法返回您的idOld函数,然而,如果可以自己对您的JSON进行字符串化(可能使用Crockford's json2.js而不是浏览器的API ),那么如果你有一串JSON,例如
var person_json = "{ \"age:\" : 20, \"isOld:\": false, isOld: function() { return this.age > 60; } }";
然后你可以打电话
eval("(" + person + ")")
,你将在json对象中找回你的函数。
答案 3 :(得分:2)
我是https://github.com/joonhocho/seri的作者。
Seri是JSON +自定义(嵌套)类支持。
您只需提供toJSON
和fromJSON
来序列化和反序列化任何类实例。
以下是嵌套类对象的示例:
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';
希望它有助于解决您的问题。
答案 4 :(得分:1)
我遇到了完全相同的问题,并编写了一个小工具来混合数据和模型。见https://github.com/khayll/jsmix
这就是你要做的:
//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());
它也可以递归地处理复杂对象,例如嵌套集合。
至于序列化JS函数,由于安全原因,我不会这样做。
答案 5 :(得分:1)
我有类似的问题,因为我找不到足够的解决方案,我还为javascript创建了一个序列化库:https://github.com/wavesoft/jbb(事实上它更多一点,因为它主要用于捆绑资源)
它接近于Binary-JSON,但它增加了一些额外的功能,例如正在编码的对象的元数据和一些额外的优化,如重复数据删除,对其他包的交叉引用和结构级压缩。 / p>
但是有一个问题:为了使捆绑包大小保持较小,捆绑包中没有类型信息。此类信息在单独的“配置文件”中提供,该配置文件描述了用于编码和解码的对象。出于优化原因,此信息以脚本形式提供。
但是,您可以使用gulp-jbb-profile
(https://github.com/wavesoft/gulp-jbb-profile)实用程序从简单的YAML对象规范生成编码/解码脚本,从而使您的生活更轻松:
# The 'Person' object has the 'age' and 'isOld'
# properties
Person:
properties:
- age
- isOld
例如,您可以查看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();
你可以这样读回来:
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'];
});
答案 6 :(得分:0)
我已将另一个JavaScript序列化器存储库添加到GitHub。
这里的方法不是将JavaScript对象序列化和反序列化为内部格式,而是将JavaScript对象序列化为本地JavaScript。这样做的好处是格式完全不受序列化程序的限制,并且可以通过调用eval()轻松地重新创建对象。
答案 7 :(得分:0)
我尝试使用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
)
}
这是什么意思?它说
$date
更好。class Klass {
static fromRepr (repr: string): Klass {
return new Klass(...)
}
static guid = '__Klass__'
__repr__ (): string {
return '...'
}
}
这是可序列化的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
)
}
我也尝试过使用Mongo风格的对象({ $date }
),但是在JSON.parse
中失败了。提供k
不再重要...
顺便说一句,如果您不关心库,则可以使用yaml.dump
中的yaml.load
/ js-yaml
。只要确保您以危险的方式进行操作即可。
答案 8 :(得分:0)
我制作了一个名为 esserializer 的 npm 模块来解决这个问题:在序列化期间以纯 JSON 格式保存 JavaScript 类对象值,而不存储任何函数。在序列化期间,唯一产生的开销是保存类名信息。因此,优化了磁盘使用。
稍后在反序列化阶段,esserializer 可以递归反序列化对象实例,保留所有类型/函数信息。它适用于浏览器和 Node.js 环境。
在 OP 的情况下,代码会很简单:
var ESSerializer = require('esserializer');
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());
// serialize
var serializedText = ESSerializer.serialize(p1);
//...do something, or send the above serializedText to another JavaScript environment.
// deserialize
var deserializedObj = ESSerializer.deserialize(serializedText, [Person]);
alert("Is old: " + deserializedObj.isOld());
deserializedObj
是一个 Person
实例,其中包含所有值/函数/超类信息。
希望对您有所帮助。