【问题标题】:Parsing json to interface in typescript and check if it is ok将 json 解析为 typescript 中的接口并检查是否正常
【发布时间】:2020-12-17 02:53:33
【问题描述】:

如何将 Json 字符串解析为嵌套接口类型?并检查是否正常?

我有一个例子,但我的模型更复杂:

export interface User = {
    name: Field;
    surname: Field;
};
export interface Field = { icon: string; text: string; visibility: boolean };
export interface Users = User[]

应该是:

export type user = {
    name: field;
    surname: field;
};
export type field = { icon: string; text: string; visibility: boolean };
export type users = user[]

或者它会是类。没关系。

这里是一个 json 示例:

[
{
"name": { "text": "David", "icon": "icon1.png", "visibility": true },
"surname": { "text": "Smith", "icon": "icon2.png", "visibility": true }
},
{
"name": { "text": "Arthur", "icon": "icon3.png", "visibility": true },
"surname": { "text": "L.", "icon": "icon6.png", "visibility": true }
},
{
"name": { "text": "Anthony", "icon": "icon1.png", "visibility": false },
"surname": { "text": "Isaacson", "icon": "icon2.png", "visibility": true }
},
{
"name": { "text": "Mike", "icon": "icon3.png", "visibility": true },
"surname": { "text": "Jobs", "icon": "icon5.png", "visibility": false }
}
]

编辑:

这里是 Chithambara 方法无效的示例:Playground

【问题讨论】:

    标签: json typescript parsing


    【解决方案1】:

    如果您的验证需求足够复杂,我会评估io-ts 之类的使用情况。它是一个用于根据代码中的元数据自动生成运行时验证的库。

    如果您的需求比较有限,您可以利用UserDefined Type Guards

    类型保护的作用是获取unknown(或any,这种函数内部确实没有区别)并告诉编译器传入的对象兼容某个界面。

    export interface Field {
      icon: string;
      text: string;
      visibility: boolean;
    }
    
    export interface User {
      name: Field;
      surname: Field;
    }
    
    function isField(obj: any): obj is Field {
      return (
        obj != null &&
        typeof obj.icon === "string" &&
        typeof obj.text === "string" &&
        typeof obj.visibility === "boolean"
      );
    }
    
    function isUser(obj: any): obj is User {
      return obj != null && isField(obj.name) && isField(obj.surname);
    
      // you can get fancy and write something like
      // return obj != null && ['name', 'surname'].every(fieldName => isField(obj[fieldName]))
    }
    
    // alternative isUser implementation, using a 
    // prototype. This will give you a compile error is the
    // interface is updated, but not this prototype.
    const userProto: User = {
      name: null,
      surname: null
    };
    
    function isUserDynamic(obj: any): obj is User {
      return obj != null && Object.keys(userProto).every(fieldName => isField(obj[fieldName]));
    }
    
    function validateUserArray(obj: any): obj is User[] {
      if (obj == null) {
        // depending upon the desired approach, you can throw an exception and bail out,
        // or simply return false.
        throw new Error("The array cannot be null");
      }
      if (!Array.isArray(obj)) return false;
    
      obj.forEach((user, i) => {
        if (!isUser(user))
          throw new Error(
            `Error at index ${i}: ${JSON.stringify(user)} is not a valid user.`
          );
      });
    
      return true;
    }
    
    const json = `[
      {
        "name": { "text": "David", "icon": "icon1.png", "visibility": true },
        "surname": { "text": "Smith", "icon": "icon2.png", "visibility": true }
      },
      {
        "name": { "text": "Arthur", "icon": "icon3.png", "visibility": true },
        "surname": { "text": "L.", "icon": "icon6.png", "visibility": true }
      },
      {
        "name": { "text": "Anthony", "icon": "icon1.png", "visibility": false },
        "surname": { "text": "Isaacson", "icon": "icon2.png", "visibility": true }
      },
      {
        "name": { "text": "Mike", "icon": "icon3.png", "visibility": true },
        "surname": { "text": "Jobs", "icon": "icon5.png", "visibility": false }
      }
    ]`;
    
    const deserialized: any = JSON.parse(json);
    
    let validatedArray: User[];
    
    if (validateUserArray(deserialized)) {
      // here deserialized is a User[], not an any.
      validatedArray = deserialized;
    }
    

    【讨论】:

    • 看起来可以,但是代码维护起来很复杂,有没有其他更易维护的方法?
    • 如果您的类型是“常规的”(即用户界面有 20 个字段,所有字段类型均为 Field),您可以将 User 设为类,使用 const proto = new User() 实例化原型并使用 Object。 keys(proto) 通过 isUser 中的return obj != null && Object.keys(proto).every(fieldName => isField(obj[fieldName])) 获取要检查的字段列表。
    • 或者,使用io-ts。当然,更易于维护,但可能不是那么简单。您还可以构建一个代码生成器,为您构建isUser 函数,并在每次用户界面更改时运行它。
    • 我添加了一个isUserDynamic 函数来替代isUser 来展示它是如何完成的,同时保持User 作为一个接口。
    猜你喜欢
    • 1970-01-01
    • 2014-04-06
    • 2015-03-11
    • 2020-11-10
    • 2016-09-29
    • 2016-02-23
    • 2021-12-25
    • 2020-04-23
    • 2020-12-03
    相关资源
    最近更新 更多