一、背景
我们在开发鸿蒙中,使用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种
- 基础数据类型问题如果没有给默认值,number为0,string为””,boolean为false
- 数组类型默认值为[]
- 对象类型为可能存在的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
最后我们来看之前存在的问题是如何解决的:
- user定义为可空类型的, 就算服务端返回的是null也没有问题
- 将属性变成readonly不允许修改值
- 基础数据类型如果没有,都给的是默认值
- 提供convertRootVONotNull方法,将对象转换成安全的RootVO
这样我们就可以快乐的写鸿蒙代码了!
四、再进一步
以上我们就实现了数据安全的处理,我把它做成了网站部署到www.jsontoarkts.cc/
大家有需要的话可以直接使用,如果有更加极端的异常case,可以反馈我会修改的!