Сериализация объекта класса 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)