Сериализация объекта класса ES6 как JSON
class MyClass {
constructor() {
this.foo = 3
}
}
var myClass = new MyClass()
Я хотел бы сериализовать объект myClass
для json.
Один простой способ, о котором я могу думать, состоит в том, что каждый член - это фактически объект javascript (массив и т.д.). Думаю, я могу поддерживать переменную для хранения переменных-членов.
this.prop.foo = this.foo
и т.д.
Я ожидал найти библиотеку toJSON/fromJSON
для объектов класса, так как я использовал их с другими языками, такими как swift/java, но не смог найти их для javascript.
Может быть, конструкция класса слишком новая, или то, что я прошу, может быть как-то легко достигнуто без библиотеки.
Ответы
Ответ 1
Как и для любого другого объекта, который вы хотите преобразовать в JS, вы можете использовать JSON.stringify
:
JSON.stringify(yourObject);
class MyClass {
constructor() {
this.foo = 3
}
}
var myClass = new MyClass()
console.log(JSON.stringify(myClass));
Ответ 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
Ответ 3
Я сталкивался с этой библиотекой, которая выполняет сериализацию и десериализацию сложных объектов (включая вложенные объекты и массивы):
https://github.com/typestack/class-transformer
У него есть как минимум два метода:
plainToClass() -> json obj to class
classToPlain() -> class to json obj
Может помочь сэкономить время для тех, кто ищет такое решение
Ответ 4
Вы должны быть в состоянии повторно инициализировать объекты. Наличие конструктора без параметров не является существенным, вы можете обойтись без него.
Вот как я выполняю глубокое копирование:
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
) this.types
членом с именем typeIndex
. После десериализации мы рекурсивно инициализируем каждую подструктуру, имеющую typeIndex
, а затем избавляемся от нее, чтобы избежать загрязнения структуры.
Ответ 5
Я сделал модуль 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()
: вручную рекурсивно составляет объект с информацией о его прототипе.
Ответ 6
Это легко, если вы не возражаете передать определение класса в декодирование.
// 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)