HarmonyOS 鸿蒙Next基于class-transformer的对象与JSON相互转换

发布于 1周前 作者 wuwangju 最后一次编辑是 5天前 来自 鸿蒙OS

HarmonyOS 鸿蒙Next基于class-transformer的对象与JSON相互转换

场景一:方法将普通javascript对象转换为特定类的实例

解决方案

首先我们先定义一个想要指定的特定的类如下:

User里面包含属性id,fistname,lastname,age,data和它的构造方法,还有特定的方法getName和isAdult。

class User {
  id: number;
  firstName: string;
  lastName: string;
  age: number;
  date?: Date;
  ​
  constructor() {
    this.id = 0;
    this.firstName = "";
    this.lastName = "";
    this.age = 0;
  }
  ​
  getName() {
    return this.firstName + ' ' + this.lastName;
  }
  ​
  isAdult() {
    return this.age > 18 && this.age < 60;
  }
}​

我们在前端处理后端传送过来的数据时,经过反序列化转换的对象全是plain(literal) object(普通(字面量)对象),如下,这里我们手动写一个待转换成特定对象的 JavaScript对象(JSON形式)。

interface UserJson {
  id: number,
  firstName: string,
  lastName: string,
  age: number,
  date?: Date
}
​
const userJson:UserJson = {
  id: 1,
  firstName: "Johny",
  lastName: "Cage",
  age: 27,
  date: new Date()
}

使用class-transform可以直接将收到的userJson字面量对象转换为User实例对象,进而可以使用其中的方法。

使用plainToInstance(将字面量转为类实例,支持数组转换)这个接口来进行转化对应实例。

plainToInstance第一个参数传入特定的实体类,第二个传入需要转换的js对象。

export function test1() {
  // JSON转为对象
  const user: User = plainToInstance(User,userJson);
  log("JSON转对象后输出情况");
  //输出class中的方法
  log(user.getName());
  log(user.isAdult() + "");
}

实现效果:

在项目中使用class-transformer例子

上面已经说明了class-transformer的最简单用法,现在说一个示例如何在项目中使用。

前端代码:

class User {
  //实体类还是我们刚才的实体User(包含fistname,lastname,age,data,和它的构造方法还有特定的方法getName和isAdult) 这里省略重复代码
}
​
Request.get({
  baseURL: 'http://xxxxx'
}).then((user: User) => {
  console.log(user)
  console.log(user.username)
  console.log(user.getName())
  console.log(user.isAdult())
})

后端代码:

[@Get](/user/Get)('/user')
findUser() {
  const user = new User('coder', '12345', 18);
  return user;
}

可以发现立马就报错了:

所以我们需要使用class-transformer的plainToInstance来做修改:

Request.get ({
  baseURL : 'http://xxx'}).then((user : User) => {
  const user1 = plainToInstance(User, user)
  console.log(user1.username)
  console.log(user1.getName())
  console.log(user1.isAdult())
})

立马见效,并且还能调用其中的方法:

场景二:JSON转对象的时候,控制某些敏感字段不对外暴露

解决方案

使用exclude注解可以解决,还是使用User实体来做演示里面的password属性不想被传到JSON。

class User {
  id: number;
  [@Exclude](/user/Exclude)()
  password: string;
  ​
  constructor(id: number, password: string) {
    this.id = id;
    this.password = password;
  }
}

手动写一个待转换成特定对象的 javascript对象(JSON形式)。

interface UserJson {
  'id': number,
  'password': string
}
 

如果是传入的字符串我们需要搭配JSON.parse来使用,将字符串转化成JSON格式的UserJson对象,使用plainToInstance来进行转换,由于使用了[@Exclude](/user/Exclude)()注解

转换成user对象后password属性也不会被输出。

export function SensitiveJsonToInstanceDemo() {
  log('JSON转对象后输出情况');
  let ss = '{"id":1,"password":"123456"}'
  let oo: UserJson = JSON.parse(ss)
  let user: User = plainToInstance(User,oo)
  log('user_id=' + user.id)
  log('user_password=' + user.password);
}

可见需要控制隐藏的password并不会被输出。

场景三:属性是一些复杂结构体时,实现json与对象的互转

解决方案

这里我们定义一个需要转换的User实体类,user的id,name 还有firstSonName,firstSonAge。

class User {
  userId: number;
  userName: string;
  firstSonName: string;
  firstSonAge: number;
  ​
  constructor(userId: number, userName: string, firstSonName: string, firstSonAge: number) {
    this.userId = userId;
    this.userName = userName;
    this.firstSonName = firstSonName;
    this.firstSonAge = firstSonAge;
  }
}

服务端传一个字符串。

{
  'userId': 1,
  'userName': 'abc',
  'children': [
    {
      'username': 'son1',
      'age': '18'
    },
  {
    'username': 'son2',
    'age': '28'
   }
  ]
}

所以我们设计两个接口:

interface UserJson {
  userId: number;
  userName: string;
  firstSonName: string;
  firstSonAge: number;
}interface FatherJson {
  userId: number;
  userName: string;
  children: Record<string,string>[]
}

直接使用plainToInstance将fatherjson转换成user对象是获取不到firstSonName,与firstSonAge的属性的,所以我们需要进行一个属性拍平。

export function ComplexJsonToInstanceDemo() {
  //服务器返回的字符串
  let ss = '{"userId":1,"userName":"abc","children":[{"username":"son1","age":"18"},{"username":"son2","age":"28"}]}'
  //将字符串转化成JSON
  let fatherjson: FatherJson = JSON.parse(ss)
  let fs: Record<string,string> = fatherjson.children[0]
  ​
  //fatherjson转userjson
  let userjson: UserJson = {
    userId: fatherjson.userId,
    userName: fatherjson.userName,
    firstSonName: fs['username'],
    firstSonAge: parseInt(fs['age'])
  }
  ​
  let user: User = plainToInstance(User,userjson)
  log('user_firstSonName=' + user.firstSonName)
  log('user_firstSonAge=' + user.firstSonAge)
}

实现效果,可看到firstSonName与firstSonAge,已经成功获取。

总结

使用三方库class-transformer搭配JSON的JSON.parse,可以满足类似gson/fastjson绝大部分的需求,更多用法可以参考https://github.com/typestack/class-transformer文档。

此外,建议审视设计的类和json是否合理,尽量避免使用复杂的转换。

8 回复
数据量太大,使用taskPool,在[@Concurrent](/user/Concurrent) 中执行
plainToClass会报错呢,为啥报 *** is not initialized

可能是你的class重写了constructor,但你的第二个参数又没有足够的字段满足你的class初始化。建议尝试使用plainToClassFromExist(new class(a,b),JSON.parse(jsonStr))

我发现并不是,一个ets中有好几个class, 但凡其中一个class被
[@Observed](/user/Observed)修饰,在
[@Concurrent](/user/Concurrent)去执行
plainToClass就会出错

是的 但是这并非库的问题 属于注解的规格问题

字符串可以转为对象数组吗

字符串到数组对象的转换通常涉及到字符串解析,这通常不在 class-transformer 的职责范围内。

第三层数组嵌套使用不了class方法

HarmonyOS(鸿蒙)系统中,对于对象与JSON的相互转换,通常会依赖于一些现成的库来简化开发过程,尽管class-transformer主要是用于TypeScript或JavaScript环境中,但类似的功能在鸿蒙系统中可以通过其他方式实现。

鸿蒙推荐使用其自己的API或第三方库(如Fastjson, Gson等,在JavaScript环境中则可能是JSON.stringify()和JSON.parse())来实现对象与JSON的转换。请检查你的项目依赖和鸿蒙的官方文档,找到最适合你项目需求的库或方法。

如果问题依旧没法解决请加我微信,我的微信是itying888。

回到顶部