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 仅支持 string、number、boolean 等基础类型。存储复杂对象(如列表、自定义类)时,必须进行序列化。

最佳实践:封装数据访问层(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 中,将复杂对象存入 AppStorage 或 PersistentStorage 时,底层都会进行 JSON 序列化。不同版本的状态管理在底层机制上存在显著差异:

  1. V1 时代的 PersistentStorage :它仅支持基础类型。存储对象或数组时,开发者必须手动调用 JSON.stringify() 转为字符串,读取时再用 JSON.parse() 还原。若对象包含方法,还需通过 map 遍历重新调用构造函数(如前文所述)。
  2. V2 时代的 PersistenceV2 :引入了"劫持-代理"双阶段机制。开发者只需使用 @ObservedV2 和 @Trace 装饰器,框架会在编译期注入拦截器。当数据发生改变时,PersistenceV2 会在后台自动进行脏值检查、JSON 序列化,并通过异步队列合并 I/O 操作写入磁盘。
    • 避坑指南 :PersistenceV2 对数据类型要求极其严苛,仅支持基础类型及其构成的 Array 或 Object。如果试图将 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 模式的流式解析器,边读取边处理节点数据。
相关推荐
Flutter_OH2 分钟前
Flutter OHOS Flutter 多设备自适应 DPI 指导文档
harmonyos
SuperHeroWu730 分钟前
【HarmonyOS 7】 应用 Agent 接入云 A2A 与端 A2A 的实践详解
ai·harmonyos·小艺
用户29469405448161 小时前
个人开发者上架鸿蒙 AI 应用,最后倒在安全评估实名制这一格
harmonyos·deepseek
Keya2 小时前
HarmonyOS 开发实战:用 util.Aspect 玩转 AOP 切面编程
harmonyos
威哥爱编程2 小时前
HarmonyOS 7 Agent 开发实战 03:Tool Calling 让 AI 查实时订单与库存
agent·harmonyos
威哥爱编程3 小时前
HarmonyOS 7 Agent 开发实战 02:Agent 工程脚手架与对话 UI 骨架
agent·harmonyos
Georgewu11 小时前
【HarmonyOS 7】 HarmonyOS 应用 Skill创建和编排详解
harmonyos
Georgewu13 小时前
【HarmonyOS 7】 应用 Agent 接入云 A2A 与端 A2A 的实践详解
harmonyos
马剑威(威哥爱编程)18 小时前
【共创稿事节】HarmonyOS 7 Agent 开发实战 05:流式输出做打字机对话
华为·harmonyos
OH_TPC21 小时前
【鸿蒙优选三方库】@ohos/coap:让鸿蒙设备说上 IoT 的母语
物联网·华为·harmonyos·鸿蒙