JSON数据处理:字符串序列化与反序列化实战(20)

在 HarmonyOS 应用开发中,JSON 数据处理是网络请求、本地数据持久化以及跨模块通信的核心基础。由于 HarmonyOS 的本地存储(如 Preferences、PersistentStorage)以及网络传输通常只支持基础数据类型(如 string),开发者必须熟练掌握对象与 JSON 字符串之间的转换。

以下梳理 JSON 序列化与反序列化实战:

一、 基础序列化与反序列化

对于简单的对象或数组,可以直接使用内置的 JSON 对象进行转换。

  • 序列化(对象转字符串) :使用 JSON.stringify()
  • 反序列化(字符串转对象) :使用 JSON.parse()

实战示例(网络请求场景)

在发送 POST 请求时,需要将请求体序列化为 JSON 字符串;在接收响应时,需要将返回的 JSON 字符串解析为具体的接口类型。

TypeScript 复制代码
import { http } from '@kit.NetworkKit';

interface User { id: number; name: string; }

async function fetchUser(userId: number): Promise<User> {
  let httpRequest = http.createHttpRequest();
  try {
    // 1. 序列化:将对象转为 JSON 字符串作为请求体
    let requestBody = JSON.stringify({ userId: userId });
    
    let result = await httpRequest.request('https://api.example.com/user', {
      method: http.RequestMethod.POST,
      header: { 'Content-Type': 'application/json' },
      extraData: requestBody
    });
    
    // 2. 反序列化:将服务端返回的 JSON 字符串解析为 User 对象
    if (result.responseCode === 200) {
      let user = JSON.parse(result.result as string) as User;
      return user;
    }
  } catch (error) {
    console.error('请求失败:', error);
    throw error;
  } finally {
    httpRequest.destroy();
  }
}

二、 高级序列化:指定部分属性

在实际业务中,有时出于安全或性能考虑,不希望将对象的所有属性都暴露或存储。JSON.stringify() 的第二个参数 replacer 支持传入一个字符串数组,用于指定需要序列化的属性。

实战示例

TypeScript 复制代码
interface Person { name: string; age: number; city: string; }
let obj: Person = { name: 'John', age: 30, city: 'ChongQing' };

// 仅序列化 name 属性
let jsonStr = JSON.stringify(obj, ['name']); 
console.info(jsonStr); // 输出: {"name":"John"}

三、 本地持久化存储中的 JSON 处理

HarmonyOS 提供的轻量级键值对存储 Preferences 仅支持 stringnumberboolean 等基础类型。存储复杂对象(如列表、自定义类)时,必须进行序列化。

最佳实践:封装数据访问层(Repo)

为了避免在业务逻辑中频繁处理字符串转换,建议将序列化逻辑封装在数据访问层中,确保上层业务始终操作的是强类型对象:

TypeScript 复制代码
export class TodayRepo {
  // 读取并反序列化
  async get(dateKey: string): Promise<DailyEntry | null> {
    const raw = await this.prefs.getString(toKey(dateKey));
    if (!raw) return null;
    try {
      const parsed = JSON.parse(raw) as DailyEntry;
      return isValidEntry(parsed) ? parsed : null; // 增加合法性校验
    } catch (_err) {
      return null;
    }
  }

  // 序列化并写入
  async set(entry: DailyEntry): Promise<void> {
    await this.prefs.setString(toKey(entry.date), JSON.stringify(entry));
  }
}

四、 核心避坑指南:带方法的对象数组

这是开发中最容易引发崩溃的场景。JSON.stringify()JSON.parse() 只能处理纯数据,无法保留对象的原型链(即类中的方法) 。如果直接将带有方法的对象数组存入 PersistentStorage,读取后调用方法会导致应用崩溃。

解决方案

  1. 无方法的纯数据类 :直接 JSON.parse() 解析即可。
  2. 带方法的类 :反序列化后,必须通过 map 遍历数据,调用类的构造函数重新实例化对象,以恢复原型链。

实战示例

TypeScript 复制代码
class Student {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  // 带有方法的类
  selfIntroduction(): string {
    return `I am ${this.name}, ${this.age} years old.`;
  }
}

// 存储时:转为字符串
let jsonStr = JSON.stringify([new Student('Tom', 16)]);

// 读取时:必须重新构造对象数组
let dataArr = JSON.parse(jsonStr);
let studentArr = dataArr.map((item: Student) => new Student(item.name, item.age));

// 此时才能安全调用方法
studentArr[0].selfIntroduction(); 

五、 特殊数据结构处理:HashMap

JSON.stringify() 默认不支持直接对 HashMap 进行操作。如果需要将 HashMap 序列化(例如传递给 Native 侧或进行网络传输),需要先将其转换为普通的 Record 对象:

TypeScript 复制代码
function map2rec(map: HashMap<string, ESObject>): Record<string, ESObject> {
  let rec: Record<string, ESObject> = {};
  map.forEach((value: ESObject, key: string) => {
    if (value instanceof HashMap) {
      rec[key] = map2rec(value); // 递归处理嵌套的 HashMap
    } else {
      rec[key] = value;
    }
  });
  return rec;
}

let myRec = map2rec(hashMap);
let str = JSON.stringify(myRec); // 正常序列化

在掌握了基础的 JSON 序列化、反序列化以及对象方法恢复等实战技巧后,针对 HarmonyOS 的企业级开发,我们还需要应对状态管理持久化、跨设备流转、第三方工具库集成以及底层字节流转换等更高级的数据处理场景。以下从四个维度进行深度扩展:

一、 状态管理持久化:V1 与 V2 的 JSON 处理差异

在 HarmonyOS 中,将复杂对象存入 AppStoragePersistentStorage 时,底层都会进行 JSON 序列化。不同版本的状态管理在底层机制上存在显著差异:

  1. V1 时代的 PersistentStorage :它仅支持基础类型。存储对象或数组时,开发者必须手动调用 JSON.stringify() 转为字符串,读取时再用 JSON.parse() 还原。若对象包含方法,还需通过 map 遍历重新调用构造函数(如前文所述)。
  2. V2 时代的 PersistenceV2 :引入了"劫持-代理"双阶段机制。开发者只需使用 @ObservedV2@Trace 装饰器,框架会在编译期注入拦截器。当数据发生改变时,PersistenceV2 会在后台自动进行脏值检查、JSON 序列化,并通过异步队列合并 I/O 操作写入磁盘。
    • 避坑指南PersistenceV2 对数据类型要求极其严苛,仅支持基础类型及其构成的 ArrayObject。如果试图将 PixelMap(图片对象)或复杂的闭包函数塞入,运行时会直接报错。

二、 跨设备流转:Base64 字节流与 JSON 的无缝转换

在 HarmonyOS NEXT 的 Share Kit(碰一碰分享)或跨设备流转(onContinue)场景中,复杂的 JSON 数据通常不能直接以明文传输,而是需要转换为高抗干扰的字节流:

  1. 发送端(序列化与编码) :首先将内存中的结构化 JSON 对象序列化为字符串,然后利用 util.TextEncoder 将其转化为 Uint8Array 字节流,最后进行 Base64 编码,拼装在自定义的外链(如 aeroplan://import_plan)中。
  2. 接收端(拦截与反序列化) :当 B 设备通过 Scheme 拦截到外链后,提取 Base64 字符串,解码还原为 JSON 字符串,再解析为具体的业务对象,最终写入本地关系型数据库(RDB)。
    这种设计将跨应用的数据流转操作大幅压缩,消除了繁琐的手动复制粘贴体验。

三、 第三方工具库集成:harmony-utils

除了系统自带的 JSON 对象,在实际工程中,推荐使用社区成熟的工具库(如 @pura/harmony-utils)来简化复杂的 JSON 操作。该库提供了丰富的 API:

  • 类型安全转换 :通过 JSONUtil.jsonToBean(jsonStr, User) 可以直接将 JSON 字符串安全地转换为指定的 Class 实例,避免了手动 as 断言的繁琐。
  • 复杂结构解析 :支持 jsonToArray(转数组)、jsonToMap(转 Map)以及 mapToJsonStr(Map 转字符串)。
  • 格式校验 :提供 isJSONStr() 方法,在处理外部传入的不可靠数据时,可先进行合法性校验,防止 JSON.parse() 抛出异常导致应用崩溃。

四、 底层数据处理:超长字符串与二进制流

在某些极客场景下(如大文件传输、长文本加密),普通的 JSON 字符串可能会遇到 URL 长度限制或内存瓶颈。此时需要结合底层 API 进行处理:

  • TextEncoder / TextDecoder :当 JSON 字符串过长时,可以利用鸿蒙的 util.TextEncoder 将字符串直接映射为 Uint8Array。这种直接内存映射操作比普通的循环转换性能高出 60% 以上,非常适合在网络请求的 extraData 中传输二进制格式的 JSON 数据。
  • 流式处理 :对于超大 JSON 文件,避免一次性 JSON.parse() 导致主线程 OOM(内存溢出),应考虑结合文件流(fs)进行分块读取,或采用 SAX 模式的流式解析器,边读取边处理节点数据。
相关推荐
狼哥16861 小时前
学习卡片案例新特性接入
ui·华为·harmonyos
小鹏linux1 小时前
鸿蒙PC迁移:Tesseract OCR C++ 三方库鸿蒙适配全记录
c++·ocr·harmonyos
JOJO数据科学1 小时前
DbGate Electron 鸿蒙 PC 适配全记录:从桌面数据库工具到 OpenHarmony HAP
数据库·electron·harmonyos
JOJO数据科学1 小时前
鸿蒙PC迁移:KTouch Qt/QML 打字训练器适配全记录
qt·华为·harmonyos
User_芊芊君子1 小时前
鸿蒙PC适配:Pinta GTK 图像编辑器鸿蒙 PC ArkWeb 适配全记录:从 .NET_GTK4 桌面到 HarmonyOS PC HAP
编辑器·.net·harmonyos
轻口味1 小时前
轻规划鸿蒙开发实战10:分布式数据同步深度博弈,UserId 隔离与并发数据冲突消解机
分布式·华为·harmonyos·鸿蒙
金启攻1 小时前
鸿蒙原生应用开发实战(五):地图可视化与性能优化——钓点地图与构建发布全攻略
harmonyos
Swift社区1 小时前
AI 接管操作系统:鸿蒙 PC AI Native OS 架构揭秘
人工智能·架构·harmonyos
knighthood20011 小时前
鸿蒙PC迁移:jieba 中文分词 Python 三方库鸿蒙PC适配全记录
python·中文分词·harmonyos