请选择 进入手机版 | 继续访问电脑版
  • 粉丝11

积分879 / 贡献0

提问0答案被采纳53文章5

作者动态

[经验分享] OpenHarmony中的fastjson gson应该这样用 原创 精华

马迪 显示全部楼层 发表于 2024-2-4 14:50:43
【问题背景】

随着越来越多的开发者开始投入北向应用的开发,无数的人开始问我:鸿蒙三方库是否有fastjson,是否有gson,当前json和对象的转换要怎么搞。

作为老牌java程序员,我的每个项目都逃不掉fastjson/gson等三方库,关键是看当前项目已经用了哪个,一般我也不会再自己新引入json库。 那么在OpenHarmony/HarmonyOS应用开发中,我的第一直觉告诉我,之前做网页/node开发时,貌似也没说有个fastjson/gson三方库。于是当时我找了下,其实在arkts开发时,沿用了js里自带的JSON,parse和JSON,stringify方法可以实现json和对象的转换,我也看了下早期确实有个开源三方库https://gitee.com/openharmony-sig/ohos_gson,我也看了下源代码,实现方式是参考gson的java代码,使用js重写。有人做过验证ohos_gson与JSON的转换的时间评测,结果是ohos_gson花费的时间是JSON的20倍以上。为了避免更多的应用入坑,因此ohos_gson到目前一直没发布到ohpm中心仓,所以别人一问我fastjson/gson的三方库的事情,我都答复用JSON去解决问题。

但是似乎问题没有完全解决,开发者越多,场景就越多。还是有很同学在反馈,说JSON并不能像fastjson/gson满足所有的需求,主要是以下几问题:
1.JSON.parse转过来的对象,不能带方法。比如class 中有个getName(){return this.firstName + this.lastName},这个方法就不能调用
2.JSON接收的json字段名称必须与本地对象属性名一致,能否重命名。
3.对象转JSON的时候,能否控制某些敏感字段不对外暴露。
4.属性是map或其他复杂结构体时,无法转换实现json与对象的互转




【解决方案】
对着上述的问题,我尝试google了一把,毕竟上述这些问题应该在web/node的开发场景下也会遇到。于是在更多的回复中,我看到业界建议更多的是用class-transformer来解决。这个库本身在OpenHarmony/HarmonyOS上正常跑,一行代码不用改,在三方库中心仓上可以直接下载到最新的0.51版本。
针对上述几个问题,我尝试了写了几段代码,验证了下,结果都是OK的。当然首先,需要

  1. ohpm install class-transformer
复制代码
那么接下来,我将给出代码,如何使用class-transformer来解决:
问题1:JSON.parse转过来的对象,不能带方法。
如以下代码所示,plainToClass可以把json转成对象后,可以调用getFullName的自定义方法
  1. import { plainToClass } from 'class-transformer';
  2.       class User {
  3.         id: number;
  4.         firstName: string;
  5.         lastName: string;
  6.         constructor() {
  7.           this.id=0
  8.           this.firstName=""
  9.           this.lastName=""
  10.         }
  11.         getFullName() {
  12.           return this.firstName + ' ' + this.lastName;
  13.         }
  14.       }

  15.       let json = "{"id": 3,"firstName": "Tracy","lastName": "Mcgrady"}"
  16.       JSON.parse(json)
  17.       let user:User = plainToClass(User, JSON.parse(json))
  18.       console.info(user.getFullName())   //输出Tracy Mcgrady
复制代码


问题2:JSON接收的json字段名称必须与本地对象属性名一致,能否重命名。

这种场景还是很常见,比如网络传送的数据与应用本身持久化或者显示的数据,名字就是可能存在不同。这里需要用到@Expose注解来重命名,如以下代码可以把id转成uid,同时可以更改自定义方法getFullName的名字
  1. import { classToPlain, Expose, plainToClass } from 'class-transformer';
  2.      interface tranUser {
  3.         uid: number,
  4.         firstName: string,
  5.         lastName: string
  6.       }


  7.        class User {
  8.         constructor() {
  9.           this.id= 0
  10.           this.firstName= ""
  11.           this.lastName= ""
  12.         }

  13.         @Expose({ name: 'uid' })
  14.         id: number;
  15.         firstName: string;
  16.         lastName: string;

  17.         @Expose({ name: 'fullName' })
  18.         getFullName() {
  19.           return this.firstName + ' ' + this.lastName;
  20.         }
  21.       }

  22.       let json = "{"uid": 10,"firstName": "CCC","lastName": "Khudoiberdiev"}"

  23.       let fromPlainUser:tranUser = JSON.parse(json)
  24.       let user:User = plainToClass(User,fromPlainUser)
  25.       console.info(JSON.stringify(user))
  26.      // 输出{"id":10,"firstName":"CCC","lastName":"Khudoiberdiev"}
  27.       console.info(JSON.stringify(classToPlain(user,{})))
  28.      //输出{"uid":10,"firstName":"CCC","lastName":"Khudoiberdiev","fullName":"CCC Khudoiberdiev"}
复制代码


3.对象转JSON的时候,能否控制某些敏感字段不对外暴露。

使用exclude注解可以搞定
  1. import { classToPlain, Exclude } from 'class-transformer';

  2.       class User {
  3.         id: number;
  4.         firstName: string;
  5.         lastName: string;
  6.         @Exclude()
  7.         password: string;

  8.         constructor() {
  9.           this.id = 0
  10.           this.firstName = "Tracy"
  11.           this.lastName = "Macgrady"
  12.           this.password = "12345"
  13.         }
  14.       }

  15.       let user: User = new User()
复制代码


4.属性是map或其他复杂结构体时,无法转换实现json与对象的互转

使用transform注解自定义方法转换搞定
  1. import { classToPlain, plainToClass, Transform, TransformationType, TransformFnParams } from 'class-transformer';

  2. //关键方法:把对象转成Map
  3.   trans(value: TransformFnParams): Map<string, string> | object {
  4.     if (value.type === TransformationType.PLAIN_TO_CLASS) {
  5.       let map: Map<string, string> = new Map()
  6.       for (let x of Object.keys(value.value)) {
  7.         map.set(x, value.value[x])
  8.       }
  9.       return map
  10.     }
  11.     else {
  12.       return value.value
  13.     }
  14.   }


  15.       class User {
  16.         id: number;
  17.         firstName: string;
  18.         lastName: string;
  19.         @Transform((value: TransformFnParams) => this.trans(value), {})
  20.         tags: Map<string, string> = new Map()

  21.         constructor() {
  22.           this.id = 0
  23.           this.firstName = ""
  24.           this.lastName = ""
  25.         }
  26.       }

  27.       let json = "{"id": 3,"firstName": "Tracy","lastName": "Mcgrady","tags":{"key1":"value1","key2":"value2"}}"
  28.       JSON.parse(json)
  29.       let user: User = plainToClass(User, JSON.parse(json))
  30.       console.info(user.tags.size.toString())\\输出2


  31.       console.info(user.tags.get("key2"))\\输出value2

  32.       console.info(JSON.stringify(user))\\输出 {"tags":{},"id":3,"firstName":"Tracy","lastName":"Mcgrady"}
  33.       console.info(JSON.stringify(classToPlain(user, {})))\\输出{"tags":{"key1":"value1","key2":"value2"},"id":3,"firstName":"Tracy","lastName":"Mcgrady"}
复制代码


【总结】

目前来看,使用系统自带JSON+三方库class-transformer,是可以满足类似gson/fastjson绝大部分的需求,还有更多的用法,可以参考https://github.com/typestack/class-transformer 的文档学会使用。

此外,建议大家审视下自己设计的类和json是否合理,尽量避免使用复杂的转换。当然也欢迎大家贡献更多的开源三方库或者其他的解决方案。

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部