31
class MyClass {
  constructor() {
    this.foo = 3
  }
}

var myClass = new MyClass()

我想将myClass对象序列化为 json。

我能想到的一种简单方法是,因为每个成员实际上都是 javascript 对象(数组等)。我想我可以维护一个变量来保存成员变量。

this.prop.foo = this.foo等等。

我希望toJSON/fromJSON为类对象找到一个库,因为我将它们与其他语言(如 swift/java)一起使用,但找不到用于 javascript 的库。

也许类构造太新了,或者我所要求的可以在没有库的情况下以某种方式轻松实现。

4

7 回答 7

29

与您想在 JS 中字符串化的任何其他对象一样,您可以使用JSON.stringify

JSON.stringify(yourObject);

class MyClass {
  constructor() {
    this.foo = 3
  }
}

var myClass = new MyClass()

console.log(JSON.stringify(myClass));

另外值得注意的是,您可以stringify通过给它一个toJSON方法来自定义如何序列化您的对象。用于在结果 JSON 字符串中表示您的对象的值将是对该toJSON对象调用方法的结果。

于 2016-10-23T10:14:02.393 回答
12

我知道这个问题很老,但我一直在睁大眼睛,直到我写了一个紧凑的、真实的、“安全的”解决方案。

反序列化返回仍然附加了工作方法的对象。

您唯一需要做的就是在序列化程序的构造函数中注册要使用的类。


class Serializer{
    constructor(types){this.types = types;}
    serialize(object) {
        let idx = this.types.findIndex((e)=> {return e.name == object.constructor.name});
        if (idx == -1) throw "type  '" + object.constructor.name + "' not initialized";
        return JSON.stringify([idx, Object.entries(object)]);
    }
    deserialize(jstring) {
        let array = JSON.parse(jstring);
        let object = new this.types[array[0]]();
        array[1].map(e=>{object[e[0]] = e[1];});
        return object;
    }
}

class MyClass {
    constructor(foo) {this.foo = foo;}
    getFoo(){return this.foo;}
}

var serializer = new Serializer([MyClass]);

console.log(serializer.serialize(new MyClass(42)));
//[0,[["foo",42]]]

console.log(serializer.deserialize('[0,[["foo",42]]]').getFoo());
//42

以上应该足以让您继续前进,但可以在此处找到更多详细信息和缩小版本。

于 2018-03-25T18:10:45.383 回答
6

我遇到了这个库,它对复杂对象(包括嵌套对象和数组)进行序列化和反序列化:

https://github.com/typestack/class-transformer

它至少有两种方法:

plainToClass() -> json obj to class
classToPlain() -> class to json obj
于 2019-02-16T18:07:42.867 回答
3

我制作了一个模块esserializer来解决这个问题。它是一个序列化 JavaScript 类实例的实用程序,并将“序列化文本”反序列化为实例对象,并保留所有类/属性/方法等。

要序列化一个实例,只需调用该serialize()方法:

const ESSerializer = require('esserializer');
let serializedString = ESSerializer.serialize(anObject);

内部机制serialize()是:将实例的属性及其类名信息递归保存成字符串。

要从字符串反序列化,只需调用该deserialize()方法,将所有涉及的类作为参数传递:

const ESSerializer = require('esserializer');
const ClassA = require('./ClassA');
const ClassB = require('./ClassB');
const ClassC = require('./ClassC');

let deserializedObj = ESSerializer.deserialize(serializedString, [ClassA, ClassB, ClassC]);

其内部机制deserialize()是:手动组合对象及其原型信息,递归。

于 2019-05-23T04:25:23.360 回答
2

如果您不介意将类定义传递给解码,这很容易。

// the code
const encode = (object) => JSON.stringify(Object.entries(object))

const decode = (string, T) => {
  const object = new T()
  JSON.parse(string).map(([key, value]) => (object[key] = value))
  return object
}

// test the code
class A {
  constructor(n) {
    this.n = n
  }

  inc(n) {
    this.n += n
  }
}

const a = new A(1)
const encoded = encode(a)
const decoded = decode(encoded, A)
decoded.inc(2)
console.log(decoded)

于 2019-06-09T00:46:19.713 回答
1

这不是一个新话题,但有一个新的解决方案:现代方法(2021 年 12 月)是使用@badcafe/jsonizerhttps ://badcafe.github.io/jsonizer

  • 与其他解决方案不同,它不会用注入的类名污染您的数据,
  • 它具体化了预期的数据层次结构。
  • 以下是 Typescript 中的一些示例,但它在 JS 中也同样适用

在展示一个类的例子之前,让我们从一个简单的数据结构开始:

const person = {
    name: 'Bob',
    birthDate: new Date('1998-10-21'),
    hobbies: [
        {   hobby: 'programming',
            startDate: new Date('2021-01-01'),
        },
        {   hobby: 'cooking',
            startDate: new Date('2020-12-31'),
        },
    ]
}
const personJson = JSON.stringify(person);
// {
//     "name": "Bob",
//     "birthDate": "1998-10-21T00:00:00.000Z",
//     "hobbies": [
//         {
//             "hobby": "programming",
//             "startDate": "2021-01-01T00:00:00.000Z"
//         },
//         {
//             "hobby": "cooking",
//             "startDate": "2020-12-31T00:00:00.000Z"
//         }
//     ]
// }
// store or send the data

请注意,日期被序列化为字符串,如果您解析该 JSON,日期将不是Date实例,它们将是Strings

现在,让我们使用 Jsonizer

// in Jsonizer, a reviver is made of field mappers :
const personReviver = Jsonizer.reviver<typeof person>({
    birthDate: Date,
    hobbies: {
        '*': {
            startDate: Date
        }
    }
});
const personFromJson = JSON.parse(personJson, personReviver);

JSON 文本中的每个日期字符串都已映射到Date解析结果中的对象。

Jsonizer 可以用递归嵌套的自定义类、第三方类、内置类或子 JSON 结构(数组、对象)无差别地恢复 JSON 数据结构(数组、对象)或类实例。

现在,让我们改用一个类:

// in Jsonizer, a class reviver is made of field mappers + an instance builder :
@Reviver<Person>({ //   bind the reviver to the class
    '.': ({name, birthDate, hobbies}) => new Person(name, birthDate, hobbies), //   instance builder
    birthDate: Date,
    hobbies: {
        '*': {
            startDate: Date
        }
    }
})
class Person {
    constructor( // all fields are passed as arguments to the constructor
        public name: string,
        public birthDate: Date
        public hobbies: Hobby[]
    ) {}
}
interface Hobby {
    hobby: string,
    startDate: Date
}

const person = new Person(
    'Bob',
    new Date('1998-10-21'),
    [
        {   hobby: 'programming',
            startDate: new Date('2021-01-01'),
        },
        {   hobby: 'cooking',
            startDate: new Date('2020-12-31'),
        },
    ]
);
const personJson = JSON.stringify(person);

const personReviver = Reviver.get(Person); //   extract the reviver from the class
const personFromJson = JSON.parse(personJson, personReviver);

最后,让我们使用 2 个类:

@Reviver<Hobby>({
    '.': ({hobby, startDate}) => new Hobby(hobby, startDate), //   instance builder
    startDate: Date
})
class Hobby {
    constructor (
        public hobby: string,
        public startDate: Date
    ) {}
}

@Reviver<Person>({
    '.': ({name, birthDate, hobbies}) => new Person(name, birthDate, hobbies), //   instance builder
    birthDate: Date,
    hobbies: {
        '*': Hobby  //   we can refer a class decorated with @Reviver
    }
})
class Person {
    constructor(
        public name: string,
        public birthDate: Date,
        public hobbies: Hobby[]
    ) {}
}

const person = new Person(
    'Bob',
    new Date('1998-10-21'),
    [
        new Hobby('programming', new Date('2021-01-01')),
        new Hobby('cooking', new Date('2020-12-31')
    ]
);
const personJson = JSON.stringify(person);

const personReviver = Reviver.get(Person); //   extract the reviver from the class
const personFromJson = JSON.parse(personJson, personReviver);
于 2021-12-22T10:39:50.520 回答
0

您需要能够递归地重新初始化对象。有一个无参数的构造函数不是必须的,你可以不用一个就可以逃脱。

这是我执行深层复制的方式:

class Serializer
{
  constructor(types){
    this.types = types;
  }

  markRecursive(object)
  {
    // anoint each object with a type index
    let idx = this.types.findIndex(t => {
      return t.name === object.constructor.name;
    });
    if (idx !== -1)
    {
      object['typeIndex'] = idx;

      for (let key in object)
      {
        if (object.hasOwnProperty(key) && object[key] != null)
          this.markRecursive(object[key]);
      }
    }
  }

  cleanUp(object)
  {
    if (object.hasOwnProperty('typeIndex')) {
      delete object.typeIndex;
      for (let key in object) {
        if (object.hasOwnProperty(key) && object[key] != null) {
          console.log(key);
          this.cleanUp(object[key]);
        }
      }
    }
  }

  reconstructRecursive(object)
  {
    if (object.hasOwnProperty('typeIndex'))
    {
      let type = this.types[object.typeIndex];
      let obj = new type();
      for (let key in object)
      {
        if (object.hasOwnProperty(key) && object[key] != null) {
          obj[key] = this.reconstructRecursive(object[key]);
        }
      }
      delete obj.typeIndex;
      return obj;
    }
    return object;
  }

  clone(object)
  {
    this.markRecursive(object);
    let copy = JSON.parse(JSON.stringify(object));
    this.cleanUp(object);
    return this.reconstructRecursive(copy);
  }
}

这个想法很简单:在序列化时,每个已知类型(位于 中的类型this.types)的成员都被一个名为typeIndex. 反序列化后,我们递归地初始化每个具有 a 的子结构typeIndex,然后去掉它以避免污染结构。

于 2019-03-20T10:43:02.957 回答