The requirement is simple. Here's a class...
class myobj {
constructor(var1, var2) {
this.var1 = var1;
this.var2 = var2;
}
addThemUp() {
return this.var1 + this.var2;
}
}
Now I make one of these...
var myInstance = new MyObj(3, 7);
Lovely. But now I need to serialize this so that I can save it and load it up later. Something like...
serialise(myInstance);
// Save to a file
// ... do some other stuff ...
// load from file
myInstance = unserialise(fileData);
// - or -
unserialise(fileData, myInstace);
// etc...
How? I don't care what form the representation takes as it will be loaded by the same page. If values-only are saved, a generic way of loading them back into an object would be fine because the same page has the class 'template' to find the definitions for the functions.
The object is large and changing, so manually copying the values from JSON in order to preserve the functions wouldn't be practically maintainable. But a solution that could do that with any number and typeof variables would be useful.
I've also got to stick to vanilla, single-file javascript, albeit in modern browsers.
TL;DR: How can I serialise and unserialise an object without losing the functions?
-
1Related, probably a dupe: Serializing an ES6 class object as JSONggorlen– ggorlen2023年02月16日 07:45:13 +00:00Commented Feb 16, 2023 at 7:45
-
This question is similar to: Best way to serialize/unserialize objects in JavaScript?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem.gre_gor– gre_gor2024年08月20日 08:09:37 +00:00Commented Aug 20, 2024 at 8:09
6 Answers 6
You can use JSON.stringify, but be aware that it only serialize properties, not methods. So to unserialize an object, just create a dummy instance and use Object.assign to update the properties using the retrieved object:
function serialize(instance) {
var str = JSON.stringify(instance);
// save str or whatever
}
function unserialize(str, theClass) {
var instance = new theClass(); // NOTE: if your constructor checks for unpassed arguments, then just pass dummy ones to prevent throwing an error
var serializedObject = JSON.parse(str);
Object.assign(instance, serializedObject);
return instance;
}
Example:
function serialize(obj) {
var str = JSON.stringify(obj);
return str;
}
function unserialize(str, theClass) {
var instance = new theClass();
var serializedObject = JSON.parse(str);
Object.assign(instance, serializedObject);
return instance;
}
// TEST CLASS
class TestClass {
constructor(a, b) {
this.a = a;
this.b = b;
}
sum() {
return this.a + this.b;
}
}
// USAGE
var instance = new TestClass(5, 7);
var str = serialize(instance);
var retrievedInstance = unserialize(str, TestClass);
console.log(retrievedInstance.sum());
2 Comments
When you serialize your object in JSON form, your class information will be lost. To prevent it from happening, you have to add a property manually to preserve your original class information; like class name. Then you can use that information bit while deserializing, and restore all information to original class object. Here is a general way to bring JSON back to its original class object:
class Foo{
constructor(var1, var2){
// save your class name as property
this.classname = this.constructor.name;
this.var1 = var1;
this.var2 = var2;
}
hoo(var3, var4){
this.var3 = var3;
this.var4 = var4;
}
serialize(){
return JSON.stringify(this);
}
}
let foo = new Foo(1, 2);
foo.hoo(3, 4);
//Now we have JSON string:
//'{"classname":"Foo","var1":1,"var2":2,"var3":3,"var4":4}'
let json = foo.serialize();
function deserialize(json){
//o is [Object object], but it contains every state of the original object
let o = JSON.parse(json);
//Get original class instance
let original_class = eval(o.classname);
//Create new object of the original class, then restore back property values
//NOTE: You may not need to pass any parameter to the constructor, since
//every state of the original object will be restored from o.
return Object.assign(new original_class(), o);
}
let _foo = deserialize(json);
console.log(_foo instanceof Foo); // returns true;
I also created a small module that works with Node.js and handle nested objects as well, so if you are interested please check the code as well.
Comments
There is no way to serialize functions.
Can set up constructor to always take an object and use Object.assign() to assign all the input to instance
Stringify the enumerable properties to json, then when you need it again parse the json and pass that stored object to new MyObj again.
class MyObj {
constructor(obj) {
const defaults = {var1: 10,var2: 10};
Object.assign(this, obj || defaults);
}
addThemUp() {
return this.var1 + this.var2 + (this.var3 || 0) + (this.var4 || 0);
}
addSomeProps() {
if (!this.var3 && !this.var4) {
this.var3 = 10;
this.var4 = 10
}
}
serialize() {
return JSON.stringify(this)
}
}
const myInstance = new MyObj();
console.log('myInstance before more props added', myInstance)
myInstance.addSomeProps()
///stringify the instance object
const json = myInstance.serialize();
// same variables needed to pass to constructor
const storedObj = JSON.parse(json)
// create new instance
const instance2 = new MyObj(storedObj);
console.log('instance 2', instance2)
// try out the methods
console.log('add in instance 2:', instance2.addThemUp())
Comments
I also need class serialization, so I made a library.
https://github.com/denostack/superserial
class User {
articles: Article[] = [];
constructor(
public name: string,
) {
}
addArticle(title: string) {
this.articles.push(new Article(this, title));
}
}
class Article {
constructor(
public user: User,
public title: string,
) {
}
}
import { Serializer } from 'superserial'
const serializer = new Serializer({
classes: {
User,
Article, // Define the class to use for deserialization here
},
});
const user = new User("wan2land");
user.addArticle("Hello World 1");
user.addArticle("Hello World 2");
const serialized = serializer.serialize(user);
console.log(serialized);
serialized string:
User{"name":"wan2land","articles":1ドル};[2,ドル3ドル];Article{"user":0,ドル"title":"Hello World 1"};Article{"user":0,ドル"title":"Hello World 2"}
const deserialized = serializer.deserialize<User>(serialized);
console.log(deserialized);
output:
<ref *1> User {
name: "wan2land",
articles: [
Article { user: [Circular *1], title: "Hello World 1" },
Article { user: [Circular *1], title: "Hello World 2" }
]
}
The class structure can be perfectly restored, and the circular reference is also possible.
Comments
I've made an npm module named esserializer to solve this problem: save JavaScript class object values during serialization, and later on recursively deserialize object instance, with all types/functions information retained.
In your case, the code would be pretty easy:
var ESSerializer = require('esserializer');
var MyObj = require('MyObj');
var myInstance = new MyObj(3, 7);
var serializedText = ESSerializer.serialize(myInstance);
//...do something, or send the above serializedText to another JavaScript environment.
var deserializedObj = ESSerializer.deserialize(serializedText, [MyObj]);
the deserializedObj is a MyObj instance, which contains all values/functions/superclasses information.
This module is an open source project. If you are interested, you can check the GitHub repo and see how it works.
2 Comments
you can use JSON.stringify(myInstance); (to serialize the object) and use JSON.parse(mymyInstance) to get it back as an object
1 Comment
Explore related questions
See similar questions with these tags.