在JavaScript中序列化/反序列化对象的最佳方法?

我的应用程序中有许多JavaScript对象,如下所示:

function Person(age) { this.age = age; this.isOld = function (){ return this.age > 60 ? true : false; } } // 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()); 

见JS小提琴 。

我的问题是:是否有一个最佳做法/模式/提示恢复我的对象在序列化之前(在这种情况下,类Person的实例)相同的types?

我有的要求:

  • 优化磁盘使用情况:我在内存中有一个很大的对象树。 所以,我不想存储function。
  • 解决scheme可以使用jQuery和另一个库来序列化/反序列化。

JSON没有数据types的function。 您只能序列化string,数字,对象,数组和布尔值(且为null

你可以创build你自己的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()); 

为了减less工作量,您可以考虑将所有需要序列化的数据存储在每个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)

这将保持toJsonfromJson方法的简单,但你必须调整你的其他function。


边注:

您应该将isOld方法添加到函数的原型中:

 Person.prototype.isOld = function() {} 

否则,每个实例都有自己的那个函数实例,这也增加了内存。

在调用JSON.stringify之后,浏览器的本地JSON API可能不会让你回到你的idOld函数, 但是 ,如果你可以自己对JSON进行string化(也许使用Crockford的json2.js代替浏览器的API),那么如果你有一串JSON

 var person_json = "{ \"age:\" : 20, \"isOld:\": false, isOld: function() { return this.age > 60; } }"; 

那么你可以打电话

 eval("(" + person + ")") 

,你会回到你的function在JSON对象。

我写了serialijse,因为我遇到了和你一样的问题。

你可以在https://github.com/erossignon/serialijsefind它;

它可以在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(); 

我有完全相同的问题,并写了一个小工具来完成数据和模型的混合。 请参阅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()); 

它可以recursion地处理复杂的对象,比如嵌套集合。

至于序列化的JSfunction,我不会做这样的事情,因为安全的原因。

我有一个类似的问题,因为我找不到一个足够的解决scheme,我也创build了一个JavaScript的序列化库: https : //github.com/wavesoft/jbb (事实上​​,它是多一点,因为它主要打算捆绑资源)

它接近于Binary-JSON,但是它增加了一些额外的function,例如正在编码的对象的元数据以及一些额外的优化,如重复数据删除,交叉引用到其他包和结构级压缩。

但有一个问题:为了保持小包的大小,捆绑中没有types信息。 这些信息是在一个单独的“configuration文件”中提供的,该configuration文件描述了用于编码和解码的对象 出于优化原因,这些信息以脚本的forms给出。

但是,您可以使用gulp-jbb-profilehttps://github.com/wavesoft/gulp-jbb-profile )实用程序,通过简单的YAML对象规范生成编码/解码脚本,从而使您的生活更轻松:

 # The 'Person' object has the 'age' and 'isOld' # properties Person: properties: - age - isOld 

例如,您可以查看jbb-profile-threeconfiguration文件。 当你准备好个人资料的时候,你可以像这样使用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']; }); 

我是https://github.com/joonhocho/seri的作者。;

Seri是JSON +自定义(嵌套)类支持。

您只需提供toJSONfromJSON即可序列化和反序列化任何类实例。

这里有一个嵌套的类对象的例子:

 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'; 

希望它有助于解决您的问题。