【问题标题】:How to convert an ES6 class to json and parse json to that class object in javascript如何将 ES6 类转换为 json 并将 json 解析为 javascript 中的该类对象
【发布时间】:2021-06-11 18:22:13
【问题描述】:

我有一些像下面这样的类,我使用该类创建了一些对象。我想将此对象转换为包含所有嵌套对象的 json。并希望将 json 返回到 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 只是做一个浅拷贝。我想要深拷贝,并且我想像以前一样将 json 字符串转换回 A 类的对象。

【问题讨论】:

  • 一个类中包含的主要是方法,将一个类转换为JSON是不合理的,因为JSON不能包含函数。最重要的是,类本身是一个函数,它根本无法转换为 JSON。

标签: javascript json es6-class stringify


【解决方案1】:

你可以让类负责它自己的序列化和反序列化。然后您可以轻松地将其转换为 JSON,因为该类应该知道它的字段以及如何转换它们。然后它应该知道应该如何将这个转换转换回它自己的另一个实例:

注意:Stack Overflow 上的可运​​行 sn-p 会将地图记录为 {},即使它有项目。检查浏览器控制台以获得更好的视图。

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

一般注意事项:

您必须注意只有 JSON 可序列化值。这包括默认值:数字、字符串、布尔值、空值、普通对象和数组。此方法A 和地图的实例添加支持。任何其他值都可能丢失或转换。这包括函数、undefined 和 BigInts,以及任何其他自定义对象。


或者,为了使对象本身与序列化/反序列化分开,您可以定义仅使用与您的类相关的数据的函数。您可以利用the replacer parameter of JSON.stringify()the reviver parameter in JSON.parse() 进行数据的遍历和转换。

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

一般注意事项:

和上面一样,这只能处理可序列化的值。

此外,反序列化具有更高的风险,因为您丢失了键值对所在的上下文。仅依靠对象的属性来确定它可能会失败的对象。考虑这个示例,其中映射中有一个名为"mapOfA" 的键和一个"name"。这应该反序列化为地图,但因为我们只知道看到普通对象版本,而没有在哪里,它被检测为A 的实例,因此引发错误:

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

比较正在发生的事情的上下文:

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

这些是大致可以采取的方法:

  • 序列化/反序列化逻辑在哪里
    • 类内部
    • 类外部
  • 对数据使用什么转换逻辑:
    • 自定义转换然后使用JSON.stringify()/JSON.parse()
    • JSON.stringify()/JSON.parse() 带有replacer/reviver 参数

当然,也可以采用一种混合其中少数几种的方法。

【讨论】:

  • 感谢 VLAZ,工作正常。你能用这种方法评论时间复杂度吗?我的实际课程非常复杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-17
  • 2017-12-27
  • 1970-01-01
相关资源
最近更新 更多