gpt4 book ai didi

json - 使用继承解析复杂的 JSON 对象

转载 作者:搜寻专家 更新时间:2023-10-30 21:34:10 27 4
gpt4 key购买 nike

我正在构建一个批处理,其中包括许多不同类型的步骤。

export interface IStep {
id: number;
icon: string;
name: string;
selected: boolean;
}

export class InitStep implements IStep {
id: number;
icon: string;
name: string;
selected = false;
}

export class InputStep implements IStep {
id: number;
icon: string;
name: string;
selected = false;
primaryKey: string;
file: File;
}

export class QueryStep implements IStep {
constructor () {
this.filters = [];
this.output_fields = [];
this.table_fields = [];
const filter = new Filter;
this.filters.push(filter);
}

get input_ids(): number[] {
return this.filters.map(filter => filter.input_id);
}

id: number;
icon: string;
name: string;
selected = false;
table: string;
table_fields: string[];
filters: Filter[];
output_fields: string[];
}

export class OutputStep implements IStep {

constructor() {
this.fields = [];
}

id: number;
icon: string;
name: string;
selected = false;
fields: string[];
}

export class DeliveryStep implements IStep {

constructor() {
this.output_ids = [];
}

id: number;
icon: string;
name: string;
selected = false;
output_ids: number[];
format: BatchOutputType;
frequency: BatchFrequencyType;
email: string;
password: string;
}

我希望能够拥有这些步骤的任意组合/数量的数组,并且能够将它们保存到本地存储并从中读取。
const key = 'notgunnawork';
localStorage.setItem(key, JSON.stringify(this.steps));
const s = JSON.parse(key) as IStep[];

我知道有一个滚雪球的机会,这会正确解析,显然解析器不知道哪些步骤最终属于哪些类。我只是想知道是否有一种简单的方法可以让我的数组以相同的方式出现。我最终会将此列表发布到服务器,并希望我的 .Net Core 代码也能够解析这个 JSON 无需我制作自定义解析器。

编辑

添加了我尝试序列化的完整类,以获取更多详细信息。每当我尝试序列化然后反序列化时,我遇到的错误是:“JSON 中的意外标记 o 位于位置 1”

最佳答案

所以,我将回答我认为您的问题,如果我错了,请随时忽略我🙂

您的问题是您有一堆带有方法的类,但是当您将这些类的实例序列化为 JSON 然后将它们反序列化回来时,您最终会得到普通的 JavaScript 对象而不是类的实例。处理此问题的一种方法是使用自定义反序列化器,它了解您的类并且可以将普通的 JavaScript 对象“水合”或“恢复”为真正的类实例。 JSON.parse()函数允许您指定一个名为 reviver 的回调参数可以用来做到这一点。

首先,我们需要建立一个系统,通过该系统,reviver 将了解您的可序列化类。我将使用 class decorator这会将每个类构造函数添加到 reviver 可以使用的注册表中。我们将要求可序列化的类构造函数可以分配给我们可以调用的类型 Serializable :它需要有一个无参数的构造函数,它构造的东西需要有一个 className属性(property):

// a Serializable class has a no-arg constructor and an instance property
// named className
type Serializable = new () => { readonly className: string }

// store a registry of Serializable classes
const registry: Record<string, Serializable> = {};

// a decorator that adds classes to the registry
function serializable<T extends Serializable>(constructor: T) {
registry[(new constructor()).className] = constructor;
return constructor;
}

现在,当你想反序列化一些 JSON 时,你可以检查序列化的东西是否有 className属性是注册表中的一个键。如果是这样,您可以在注册表中使用该类名的构造函数,并通过 Object.assign() 将属性复制到其中。 :
// a custom JSON parser... if the parsed value has a className property
// and is in the registry, create a new instance of the class and copy
// the properties of the value into the new instance.
const reviver = (k: string, v: any) =>
((typeof v === "object") && ("className" in v) && (v.className in registry)) ?
Object.assign(new registry[v.className](), v) : v;

// use this to deserialize JSON instead of plain JSON.parse
function deserializeJSON(json: string) {
return JSON.parse(json, reviver);
}

好的,现在我们有了,让我们创建一些类。 (在您编辑之前,我在这里使用您的原始定义。)请注意,我们需要添加 className属性,我们必须有一个无参数构造函数(如果你不指定构造函数,这会免费发生,因为 default constructor 是无参数的):
// mark each class as serializable, which requires a className and a no-arg constructor
@serializable
class StepType1 implements IStep {
id: number = 0;
name: string = "";
prop1: string = "";
readonly className = "StepType1"
}

@serializable // error, property className is missing
class OopsNoClassName {

}

@serializable // error, no no-arg constructor
class OopsConstructorRequiresArguments {
readonly className = "OopsConstructorRequiresArguments"
constructor(arg: any) {

}
}

@serializable
class StepType2 implements IStep {
id: number = 0;
name: string = "";
prop2: string = "";
prop3: string = "";
prop4: string = "";
readonly className = "StepType2"
}

@serializable
class StepType3 implements IStep {
id: number = 0;
name: string = "";
prop5: string = "";
prop6: string = "";
readonly className = "StepType3"
}

现在让我们测试一下。像往常一样制作一些对象,并将它们放入数组中:
// create some objects of our classes
const stepType1 = new StepType1();
stepType1.id = 1;
stepType1.name = "Alice";
stepType1.prop1 = "apples";

const stepType2 = new StepType2();
stepType2.id = 2;
stepType2.name = "Bob";
stepType2.prop2 = "bananas";
stepType2.prop3 = "blueberries";
stepType2.prop4 = "boysenberries";

const stepType3 = new StepType3();
stepType3.id = 3;
stepType3.name = "Carol";
stepType3.prop5 = "cherries";
stepType3.prop6 = "cantaloupes";

// make an array of IStep[]
const arr = [stepType1, stepType2, stepType3];

让我们有一个函数来检查数组的元素并检查它们是否是您的类的实例:
// verify that an array of IStep[] contains class instances
function verifyArray(arr: IStep[]) {
console.log("Array contents:\n" + arr.map(a => {
const constructorName = (a instanceof StepType1) ? "StepType1" :
(a instanceof StepType2) ? "StepType2" :
(a instanceof StepType3) ? "StepType3" : "???"
return ("id=" + a.id + ", name=" + a.name + ", instanceof " + constructorName)
}).join("\n") + "\n");
}

让我们确保它适用于 arr :
// before serialization, everything is fine
verifyArray(arr);
// Array contents:
// id=1, name=Alice, instanceof StepType1
// id=2, name=Bob, instanceof StepType2
// id=3, name=Carol, instanceof StepType3

然后我们序列化它:
// serialize to JSON
const json = JSON.stringify(arr);

为了演示您的原始问题,让我们看看如果我们只使用 JSON.parse() 会发生什么情况没有复活者:
// try to deserialize with just JSON.parse
const badParsedArr = JSON.parse(json) as IStep[];

// uh oh, none of the deserialized objects are actually class instances
verifyArray(badParsedArr);
// Array contents:
// id=1, name=Alice, instanceof ???
// id=2, name=Bob, instanceof ???
// id=3, name=Carol, instanceof ???

如您所见, badParsedArr 中的对象确实有 idname属性(以及其他特定于类的实例属性,如 prop3,如果您选中),但它们不是您的类的实例。

现在我们可以使用我们的自定义反序列化器查看问题是否已解决:
// do the deserialization with our custom deserializer
const goodParsedArr = deserializeJSON(json) as IStep[];

// now everything is fine again
verifyArray(goodParsedArr);
// Array contents:
// id=1, name=Alice, instanceof StepType1
// id=2, name=Bob, instanceof StepType2
// id=3, name=Carol, instanceof StepType3

是的,它有效!

上面的方法很好,但有一些注意事项。主要的:如果你的可序列化类包含本身可序列化的属性,它就会起作用,只要你的对象图是 tree ,其中每个对象只出现一次。但是如果你有一个带有任何类型的对象图 cycle在其中(意味着如果您以多种方式遍历图形,则同一对象会出现多次),那么您将获得意想不到的结果。例如:
const badArr = [stepType1, stepType1];
console.log(badArr[0] === badArr[1]); // true, same object twice
const badArrParsed = deserializeJSON(JSON.stringify(badArr));
console.log(badArrParsed[0] === baddArrParsed[1]); // false, two different objects

在上述情况下,同一个对象出现多次。序列化和反序列化数组时,新数组包含两个具有相同属性值的不同对象。如果您需要确保对任何特定对象只反序列化一次,那么您需要一个更复杂的 deserialize()跟踪某些唯一属性(如 id )并返回现有对象而不是创建新对象的函数。

其他注意事项:这假设您的可序列化类具有仅由其他可序列化类以及 JSON 友好值(如字符串、数字、数组、普通对象和 null)组成的实例属性。 .如果你使用其他东西,比如 Date s,您将不得不处理这些序列化为字符串的事实。

序列化/反序列化的复杂程度在很大程度上取决于您的用例。

好的,希望有帮助。祝你好运!

关于json - 使用继承解析复杂的 JSON 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54427218/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com