簡體   English   中英

將 ES6 類對象序列化為 JSON

[英]Serializing an ES6 class object as JSON

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

var myClass = new MyClass()

我想將myClass對象序列化為 json。

我能想到的一種簡單方法是,由於每個成員實際上都是 javascript 對象(數組等)。我想我可以維護一個變量來保存成員變量。

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

我希望為類對象找到一個toJSON/fromJSON庫,因為我將它們與其他語言(例如 swift/java)一起使用,但找不到用於 javascript 的庫。

也許類構造太新,或者我所要求的可以在沒有庫的情況下以某種方式輕松實現。

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

JSON.stringify(yourObject);

 class MyClass { constructor() { this.foo = 3 } } var myClass = new MyClass() console.log(JSON.stringify(myClass));

另外值得注意的是,您可以通過給它一個toJSON方法來自定義stringify如何序列化您的對象。 用於在結果 JSON 字符串中表示對象的值將是對該對象調用toJSON方法的結果。

我知道這個問題很老,但我一直在抓緊眼睛,直到我寫出一個緊湊的、真實的、“安全的”解決方案。

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

您唯一需要做的就是在序列化程序的構造函數中注冊要使用的類。


 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

以上應該足以讓您繼續前進,但可以在此處找到更多詳細信息和縮小版本。

我遇到了這個庫,它可以對復雜對象(包括嵌套對象和數組)進行序列化和反序列化:

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

它至少有兩種方法:

plainToClass() -> json obj to class
classToPlain() -> class to json obj

我制作了一個模塊序列化器來解決這個問題。 它是一個用於序列化 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()的內部機制是:手動組合對象及其原型信息,遞歸。

如果您不介意將類定義傳遞給 decode,這很容易。

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

不是一個新話題,但有一個新的解決方案:現代方法(2021 年 12 月)是使用@badcafe/jsonizerhttps : @badcafe/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實例,而是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);

您需要能夠遞歸地重新初始化對象。 擁有一個無參數的構造函數並不是必不可少的,你可以不用它就離開。

這是我執行深復制的方式:

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子結構,然后將其刪除以避免污染結構。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM