JavaScript类对象序列化?

11

这个要求很简单。这里有一个类...

class myobj {

    constructor(var1, var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    addThemUp() {
        return this.var1 + this.var2;
    }

}

现在我制作其中之一...

var myInstance = new MyObj(3, 7);

非常好。但现在我需要对此进行序列化,以便稍后保存和加载它。类似于...

serialise(myInstance);
// Save to a file
// ... do some other stuff ...
// load from file
myInstance = unserialise(fileData);
// - or - 
unserialise(fileData, myInstace);
// etc...

如何在不丢失函数的情况下序列化和反序列化对象?我不关心表示形式采用什么形式,因为它将由同一页加载。如果只保存值,则可以以通用方式将其加载回对象,因为同一页具有“模板”类来查找函数的定义。

该对象很大且易变,因此手动从JSON中复制值以保留函数将不太可维护。但是,任意数量和typeof变量都可以执行此操作的解决方案将非常有用。

我还必须坚持使用基本的、单个文件的JavaScript,尽管在现代浏览器中。

TL;DR: 如何在序列化和反序列化对象时不丢失函数?


1
相关问题,可能是重复的:将ES6类对象序列化为JSON - ggorlen
7个回答

17

您可以使用JSON.stringify,但请注意它只序列化属性而不是方法。因此,要反序列化对象,只需创建一个虚拟实例,并使用Object.assign来使用获取到的对象更新属性:

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

例子:

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());


使用Object.assign和JSON.stringify可以很好地处理具有本地类型变量的对象,非常感谢这个。但是,对于此对象内部还具有函数的对象递归工作是否可行,或者我必须自己遍历所拥有的对象并进行分配?如果我这样做,也不是世界末日,至少我不会为其中每个变量都这样做! - IamPancakeMan
@IamPancakeMan 我制作了一个名为esserializer的npm模块,可以递归反序列化对象实例,并保留所有类型/函数信息。我很快会在这里发布这个解决方案的答案。 - shaochuancs

2

当您将对象序列化为JSON格式时,类信息将会丢失。 为了防止这种情况发生,您需要手动添加一个属性来保留原始的类信息,例如类名。 然后在反序列化时可以使用该信息位,并将所有信息还原为原始的类对象。 以下是将JSON恢复为其原始类对象的一般方法:

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;

我还创建了一个适用于Node.js的小模块jsclass-serializer,可以处理嵌套对象。如果你感兴趣,请查看代码。

1
无法序列化函数。
可以设置构造函数始终接受一个对象,并使用 Object.assign() 将所有输入分配给实例。
将可枚举属性转换为 JSON 字符串,然后在需要时解析 JSON 并将存储的对象再次传递给 new MyObj

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())


1

0
我创建了一个名为esserializer的npm模块来解决这个问题:在序列化期间保存JavaScript类对象值,并在以后递归地反序列化对象实例,保留所有类型/函数信息。
在你的情况下,代码会相当简单:
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]);

deserializedObj 是一个 MyObj 实例,其中包含所有值/函数/超类信息。

这个模块是一个开源项目。如果您感兴趣,可以查看 GitHub 仓库 并了解它的工作原理。


也许这个项目对你也很有兴趣:https://github.com/typestack/class-transformer - Tchakabam
还想知道,这个项目是否解决了这篇文章中描述的JS/TS序列化方面的一些问题?https://medium.com/@aems/one-mans-struggle-with-typescript-class-serialization-478d4bbb5826 - Tchakabam

0

我还需要类序列化,所以我写了一个库。

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

序列化字符串:

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

输出:

<ref *1> User {
  name: "wan2land",
  articles: [
    Article { user: [Circular *1], title: "Hello World 1" },
    Article { user: [Circular *1], title: "Hello World 2" }
  ]
}

类结构可以完美地恢复,循环引用也是可能的。


0
你可以使用 JSON.stringify(myInstance)(将对象序列化)并使用 JSON.parse(mymyInstance) 将其作为对象获取回来。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接