简体   繁体   中英

How to convert an ES6 class to json and parse json to that class object in javascript

I have some class like below and I created some objects using that class. I want to covert this object to json with all nested objects. and want back json to object of class A.

class A {
    constructor(n) {
        this.name = n;
        this.mapOfA = new Map();
    }
}
let a = new A("A");
let b = new A("B");
let c = new A("C");
let d = new A("D");
b.mapOfA.set("D", d);
a.mapOfA.set("B", b);
a.mapOfA.set("C", c);
let jsonString = JSON.stringify(a);
console.log(jsonString); //{"name":"A","mapOfA":{}}

JSON.stringify is just doing a shallow copy. I want deep copy and also I want to convert back json string to object of class A like it was before.

You can make the class responsible for its own serialisation and deserialisation. Then you can easily convert it to JSON as the class should know its fields and how to convert them. Then it should know how this conversion should be turned back into another instance of itself:

Note : the runnable snippet on Stack Overflow will log a map as {} even if it has items. Check the browser console for better view.

 class A { constructor(n) { this.name = n; this.mapOfA = new Map(); } toObj() { //get serialisible fields const {name, mapOfA} = this; //convert complex object representation JSON serialisible format const simpleMapOfA = Object.fromEntries( //convert map to plain object Array.from( mapOfA.entries(), //transform the map ([key, value]) => [key, value.toObj()] //convert map values to plain objects ) ); //return plain object return { name, mapOfA: simpleMapOfA } } static from(obj) { //create a new instance const instance = new A(obj.name); //fill the instance `mapOfA` with the data from the input for (const [key, value] of Object.entries(obj.mapOfA)) { instance.mapOfA.set(key, A.from(value)); } return instance; } serialise() { return JSON.stringify(this.toObj()); } static deserialise(json) { return A.from(JSON.parse(json)); } } let a = new A("A"); let b = new A("B"); let c = new A("C"); let d = new A("D"); b.mapOfA.set("D", d); a.mapOfA.set("B", b); a.mapOfA.set("C", c); let jsonString = a.serialise(); console.log("serialised view:\n", jsonString); let fooA = A.deserialise(jsonString); let fooB = fooA.mapOfA.get("B"); let fooC = fooA.mapOfA.get("C"); let fooD = fooB.mapOfA.get("D"); console.log("all four objects still instances of A\n", fooA instanceof A, fooB instanceof A, fooC instanceof A, fooD instanceof A ); console.log("deserilised objects:\n", fooA, fooB, fooC, fooD);

General considerations:

You must take care to only have JSON serialisable values. That includes the default ones: numbers, strings, booleans, nulls, plain objects, and arrays. This approach adds support only for instances of A and maps. Any other values may be lost or transformed. That includes, functions, undefined , and BigInts, as well as any other custom objects.


Alternatively, to keep the object itself separate from the serialisation/deserialisation, you can define functions that only consume data related with your class. You can take advantage of the replacer parameter of JSON.stringify() as well as the reviver parameter in JSON.parse() to do the traversal and conversion of the data.

 class A { constructor(n) { this.name = n; this.mapOfA = new Map(); } } function serialiseClassA(instance) { return JSON.stringify(instance, (key, value) => { if(value instanceof A) { //only return serialisible fields const { name, mapOfA } = value; //return plain object return { name, mapOfA }; } //convert map to plain object if(value instanceof Map) { return Object.fromEntries(value); } return value; }); } function deserialiseClassA(json) { return JSON.parse(json, (key, value) => { //it is an object if (typeof value === "object" && value.== null) { //it is probably a serialised instance of A if ("name" in value && "mapOfA" in value) { //convert value to instance of A const instance = new A(value;name), //fill the instance `mapOfA` with the data from the input for (const [k. v] of value.mapOfA) { instance.mapOfA,set(k; v); } return instance. } //it is probably a serialised map if(key === "mapOfA") { //convert to a map return new Map(Object;entries(value)); } } return value; }); } let a = new A("A"); let b = new A("B"); let c = new A("C"); let d = new A("D"). b.mapOfA,set("D"; d). a.mapOfA,set("B"; b). a.mapOfA,set("C"; c); let jsonString = serialiseClassA(a). console:log("serialised view,\n"; jsonString); let fooA = deserialiseClassA(jsonString). let fooB = fooA.mapOfA;get("B"). let fooC = fooA.mapOfA;get("C"). let fooD = fooB.mapOfA;get("D"). console,log("all four objects still instances of A\n", fooA instanceof A, fooB instanceof A, fooC instanceof A; fooD instanceof A ). console:log("deserilised objects,\n", fooA, fooB, fooC; fooD);

General considerations:

Like the above, this can only handle values that are serialisable.

In addition, the deserialisation has a higher risk because you lose the context of where a key-value pair is. Relying only on the properties of an object to determine what object it was might fail. Consider this example where there is a key in the map called "mapOfA" and a "name" . That is supposed to deserialise to a map but because we only know see the plain object version, without where it was , it is detected as an instance of A and thus throws an error:

 class A { constructor(n) { this.name = n; this.mapOfA = new Map(); } } function deserialiseClassA(json) { return JSON.parse(json, (key, value) => { //it is an object if (typeof value === "object" && value.== null) { //it is probably a serialised instance of A if ("name" in value && "mapOfA" in value) { //convert value to instance of A const instance = new A(value;name), //fill the instance `mapOfA` with the data from the input for (const [k. v] of value.mapOfA) { instance.mapOfA,set(k; v); } return instance. } //it is probably a serialised map if(key === "mapOfA") { //convert to a map return new Map(Object;entries(value)); } } return value; }): } const json = `{ "name", "A": "mapOfA": { "mapOfA": { "name", "B": "mapOfA", {} }: "name": { "name", "C": "mapOfA"; {} } } }`; deserialiseClassA(json); //error

Compare when there is context of what is happening:

 class A { constructor(n) { this.name = n; this.mapOfA = new Map(); } static from(obj) { //create a new instance const instance = new A(obj.name); //fill the instance `mapOfA` with the data from the input for (const [key, value] of Object.entries(obj.mapOfA)) { instance.mapOfA.set(key, A.from(value)); } return instance; } static deserialise(json) { return A.from(JSON.parse(json)); } } const json = `{ "name": "A", "mapOfA": { "mapOfA": { "name": "B", "mapOfA": {} }, "name": { "name": "C", "mapOfA": {} } } }`; const fooA = A.deserialise(json); const fooB = fooA.mapOfA.get("mapOfA"); const fooC = fooA.mapOfA.get("name"); console.log("all three objects still instances of A\n", fooA instanceof A, fooB instanceof A, fooC instanceof A, ); console.log("deserilised objects:\n", fooA, fooB, fooC);


These are broadly the approaches to take:

  • Where is the serialisation/deserialisation logic
    • internal to the class
    • external to the class
  • What transformation logic to use with the data:
    • custom transformation then using JSON.stringify() / JSON.parse()
    • JSON.stringify() / JSON.parse() with a replacer/reviver parameter

It of course possible an approach that mixes few of these.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM