将 ES6 类对象序列化为 JSON
Posted
技术标签:
【中文标题】将 ES6 类对象序列化为 JSON【英文标题】:Serializing an ES6 class object as JSON 【发布时间】:2017-03-05 05:41:52 【问题描述】:class MyClass
constructor()
this.foo = 3
var myClass = new MyClass()
我想将myClass
对象序列化为 json。
我能想到的一种简单方法是,因为每个成员实际上都是 javascript 对象(数组等)。我想我可以维护一个变量来保存成员变量。
this.prop.foo = this.foo
等等。
我希望为类对象找到一个 toJSON/fromJSON
库,因为我将它们与其他语言(如 swift/java)一起使用,但找不到用于 javascript 的库。
也许类结构太新,或者我的要求可以在没有库的情况下以某种方式轻松实现。
【问题讨论】:
你听说过JSON.stringify()
吗?这符合你的要求吗?
您有这样做的理由吗?
@Pineda 是的,我想将可序列化的数据存储在 react redux 存储中。但是,答案似乎表明我可以将我的类对象存储在 redux 中,因为它已经是可序列化的。
相关:反序列化:***.com/questions/38922990/…
【参考方案1】:
与您想在 JS 中字符串化的任何其他对象一样,您可以使用JSON.stringify
:
JSON.stringify(yourObject);
class MyClass
constructor()
this.foo = 3
var myClass = new MyClass()
console.log(JSON.stringify(myClass));
另外值得注意的是,您可以自定义stringify
如何序列化您的对象,方法是给它一个toJSON
method。用于在结果 JSON 字符串中表示您的对象的值将是对该对象调用 toJSON
方法的结果。
【讨论】:
如何反序列化它。 @IWuZuoJSON.parse( string )
谢谢。 JSON.parse(string) 返回一个 json 对象。不是 MyClass 的实例。
@IWuZhuo 为此,我建议您在类上创建一个静态方法,该方法接受该对象并通过创建具有 JSON 中属性的对象来返回该类的实例。开箱即用的 Javascript 无法做到这一点,但我会说自己编写很容易
要反序列化,它并不是那么简单,尤其是在涉及复杂的嵌套对象时,但是有一个轻量级库可以做到这一点。详情请查看我的回答。【参考方案2】:
我知道这个问题已经很老了,但我一直在睁大眼睛,直到我写了一个紧凑的、真实的、“安全的”解决方案。
反序列化返回仍然具有附加工作方法的对象。
您唯一需要做的就是在序列化器的构造函数中注册要使用的类。
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
以上内容应该足以让您继续前进,但可以找到更多详细信息和缩小版here。
【讨论】:
此示例不处理递归对象初始化。如果类Person
包含成员Address
,反序列化后你将无法调用Address
的方法。【参考方案3】:
我遇到过这个库,它可以对复杂对象(包括嵌套对象和数组)进行序列化和反序列化:
https://github.com/typestack/class-transformer
它至少有两种方法:
plainToClass() -> json obj to class
classToPlain() -> class to json obj
【讨论】:
【参考方案4】:我制作了一个模块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()
的内部机制是:手动组合对象及其原型信息,递归。
【讨论】:
replit.com/@deanc1/SickOblongMicroinstruction#index.js 这适用于一级深层嵌套对象 - 谢谢@shaochuancs【参考方案5】:如果您不介意将类定义传递给解码,这很容易。
// 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)
【讨论】:
如果您不使用嵌套对象,这是一个很好的解决方案。但是,当您尝试这样做时,您会看到失败:replit.com/@deanc1/PerfumedBustlingAngle#index.js【参考方案6】:不是一个新话题,但有一个新的解决方案:现代方法(2021 年 12 月)是使用 @badcafe/jsonizer
: https://badcafe.github.io/jsonizer
在展示一个类的例子之前,让我们从一个简单的数据结构开始:
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
实例,它们将是 String
s
现在,让我们使用 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);
【讨论】:
【参考方案7】:您需要能够递归地重新初始化对象。有一个无参数的构造函数不是必须的,你可以不用它。
这是我执行深层复制的方式:
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
的成员涂抹。反序列化后,我们递归地初始化每个有typeIndex
的子结构,然后去掉它以避免污染结构。
【讨论】:
以上是关于将 ES6 类对象序列化为 JSON的主要内容,如果未能解决你的问题,请参考以下文章
如何将具有不同值的相同 JSON 对象反序列化为 java 类