鸿蒙JSON数据安全解析工具,JsonToArkTS来了

290 阅读5分钟
pankaj-patel-_SgRNwAVNKw-unsplash.jpg

一、背景

我们在开发鸿蒙中,使用ArkTS时经常会遇到一些类型定义的问题,特别是像客户端开发中,我们明明定义了是基础数据类型,但就是返回的是undefined,如果不解决这样的问题,都不知道该如何相信谁,难道把所有的类型都要当成可空类型来做吗,如果那样的话代码里的无效判断太多不说,就是鸿蒙开发体验也会大打折扣。

所以我们需要一个通用,自动化的方案来解决这个问题。

直接先给网站地址:www.jsontoarkts.cc/

二、JSON解析存在的问题

先回想一下,我们在Android开发中如何进行的JSON解析呢?谷歌的Gson都替我们做好了,我们直接使用,它本身就支持默认值的处理,空判断等所以我们不需要再进行一次处理了,那我们在ArkTS中如何进行JSON转换呢?

从一个例子来看:

{
  "user": {
    "name": "张三",
    "age": null,
    "contacts": [
      {
        "type": "email",
        "value": "zhangsan@example.com"
      }
    ],
    "address": {
      "city": "北京",
      "street": null
    }
  }
}

如果我们将它作为我们服务端请求的数据,那我们在我们的ArkTS中如何定义和使用呢?那么我像我们的定义一般都是会这样的

interface RootVO {
  user: UserVO;
}

/**
* UserVO 接口定义
*/
export interface UserVO {
  /** name */
  name: string;
  /** age */
  age: number;
  /** contacts */
  contacts: ContactsVO[];
  /** address 对象 */
  address: AddressVO;
}

/**
* ContactsVO 接口定义
*/
export interface ContactsVO {
  /** type */
  type: string;
  /** value */
  value: string;
}

/**
* AddressVO 接口定义
*/
export interface AddressVO {
  /** city */
  city: string;
  /** street */
  street: string;
}

定义完如上数据之后我们就可以通过

let s = "{\n" +
          "  \"user\": {\n" +
          "    \"name\": \"张三\",\n" +
          "    \"age\": null,\n" +
          "    \"contacts\": [\n" +
          "      {\n" +
          "        \"type\": \"email\",\n" +
          "        \"value\": \"zhangsan@example.com\"\n" +
          "      }\n" +
          "    ],\n" +
          "    \"address\": {\n" +
          "      \"city\": \"北京\",\n" +
          "      \"street\": null\n" +
          "    }\n" +
          "  }\n" +
          "}"
let rootVO = JSON.parse(s) //将JSON字符串变成JSON对象

let user = rootVO.user

let userName = rootVO.user.name

基本上我们通过这样的方法就可以获取到JSON对象了,但是我们思考几个问题,

  • 如果这个user服务端返回的是null呢?
  • 如果age服务端返回的是null,而非number呢?
  • 如果addres没返回呢?

这样我们就可以看出来,正确的数据那么这个解析和使用没有问题,如果是遇到我说的几个问题,那么整体逻辑就有问题了,比如说:

let rootVO = JSON.parse(s) //将JSON字符串变成JSON对象

let user = rootVO.user // 如果user返回为null

let userName = rootVO.user.name // 这个userName就是undefined了

有同学说那我们可以先进行判断再使用啊,就像这样:

if (rootVO.user && root.user.name) { // 预先进行判断处理
    let userName = rootVO.user.name  
}

这样确实不会有问题了,但是我们的代码里就会有很多的if判断逻辑,这种无用的逻辑就相当于噪音代码,让我们的代码很冗余,看的人头晕眼花。

那有没有一种方式,使用的时候像Gson一样都帮我做好了,或者JSON解析后再处理一遍呢?

三、数据安全处理

上面抛出的问题,我们可以总结下,我们想要的情形无非就是这么3种

  1. 基础数据类型问题如果没有给默认值,number为0,string为””,boolean为false
  2. 数组类型默认值为[]
  3. 对象类型为可能存在的null我们应该定义为可空的,默认值为null

然后我们需要一个方法来在JSON解析后再转换一遍,这样还是JSON解析库还是可以随便使用,不耦合,成本最小,最后来看下我们的定义的数据类型和安全转换的方法吧:

/**
* ContactsVO 接口定义
*/
export interface ContactsVO {
  /** type */
  readonly type: string;
  /** value */
  readonly value: string;
}

/**
* AddressVO 接口定义
*/
export interface AddressVO {
  /** city */
  readonly city: string;
  /** street */
  readonly street: string;
}

/**
* UserVO 接口定义
*/
export interface UserVO {
  /** name */
  readonly name: string;
  /** age */
  readonly age: number;
  /** contacts */
  readonly contacts: ContactsVO[];
  /** address 对象 */
  readonly address: AddressVO | null;
}

/**
* RootVO 接口定义
*/
export interface RootVO {
  /** user 对象 */
  readonly user: UserVO | null;
}

/**
* 转换 ContactsVO,确保所有字段都有默认值
* @param t 可选的 ContactsVO 对象
* @returns 转换后的 ContactsVO 对象,确保所有字段都有值
*/
export function convertContactsVONotNull(t?: ContactsVO | null): ContactsVO {
  return {
    type: t?.type ?? "",
    value: t?.value ?? ""
  };
}

/**
* 转换 AddressVO,确保所有字段都有默认值
* @param t 可选的 AddressVO 对象
* @returns 转换后的 AddressVO 对象,确保所有字段都有值
*/
export function convertAddressVONotNull(t?: AddressVO | null): AddressVO {
  return {
    city: t?.city ?? "",
    street: t?.street ?? ""
  };
}

/**
* 转换 UserVO,确保所有字段都有默认值
* @param t 可选的 UserVO 对象
* @returns 转换后的 UserVO 对象,确保所有字段都有值
*/
export function convertUserVONotNull(t?: UserVO | null): UserVO {
  return {
    name: t?.name ?? "",
    age: t?.age ?? 0,
    contacts: Array.isArray(t?.contacts) ? t?.contacts?.map(item => convertContactsVONotNull(item)) ?? [] : [],
    address: t?.address ? convertAddressVONotNull(t?.address) : null
  };
}

/**
* 转换 RootVO,确保所有字段都有默认值
* @param t 可选的 RootVO 对象
* @returns 转换后的 RootVO 对象,确保所有字段都有值
*/
export function convertRootVONotNull(t?: RootVO | null): RootVO {
  return {
    user: t?.user ? convertUserVONotNull(t?.user) : null
  };
}

然后我们使用就是这样了:

let rootVONull = JSON.parse(json) as RootVO
let rootVONotNull = convertRootVONotNull(rootVONull)
console.log("rootVONotNull", rootVONotNull.user?.age); // 虽然服务端是null但结果是输出0

最后我们来看之前存在的问题是如何解决的:

  1. user定义为可空类型的, 就算服务端返回的是null也没有问题
  2. 将属性变成readonly不允许修改值
  3. 基础数据类型如果没有,都给的是默认值
  4. 提供convertRootVONotNull方法,将对象转换成安全的RootVO

这样我们就可以快乐的写鸿蒙代码了!

四、再进一步

以上我们就实现了数据安全的处理,我把它做成了网站部署到www.jsontoarkts.cc/

大家有需要的话可以直接使用,如果有更加极端的异常case,可以反馈我会修改的!