HarmonyOS 应用开发之JSON 解析与 ArkTS 序列化详解
JSON 解析与 ArkTS 序列化
一、引言
JSON 是应用数据流转的事实标准:服务端响应、本地缓存、组件 key 生成、调试日志输出都离不开它。在多设备短视频项目中,JSON.stringify 被大量用于列表 key 生成与日志输出,而 JSON.parse 则是网络数据(第 38 篇)进入模型层(第 36 篇)的必经之路。
但 JSON 与 ArkTS 的"对象"并不等价:JSON 只有对象、数组、字符串、数字、布尔与 null 六种类型,而 ArkTS 的 class 可以携带方法、联合类型字段与运行时资源引用。从 JSON 到类型安全的模型实例,需要一套明确的转换规范。本文结合项目真实用法,讲解 JSON.parse/stringify 基础、class 类型安全解析、复杂嵌套结构、ArkTS 序列化限制以及 mock/网络数据的转换路径。

二、JSON.parse 与 JSON.stringify 基础
两个 API 互为逆操作:stringify 把对象序列化为字符串,parse 把字符串还原为对象。项目中最常见的用法是作为列表 key 与日志输出:
// features/multishortvideoindividual/src/main/ets/view/Works.ets(节选)
Repeat<WorksDataModel>(this.worksData)
.each((item: RepeatItem<WorksDataModel>) => {
// ...GridItem 渲染
})
.key((item: WorksDataModel) => JSON.stringify(item)) // 用序列化结果做唯一 key
// features/multishortvideoadaptivevideo/src/main/ets/view/AdaptiveAVPlayer.ets(节选)
Logger.info(TAG, `changePortraitVideo immersionInfo = ${JSON.stringify(immersionInfo)}`);
stringify 的序列化规则值得牢记:默认仅序列化可枚举的自身属性;undefined、函数、Symbol 会被静默省略;NaN 与 Infinity 转为 null;数组中的 null/undefined 保留为 null。这些规则决定了"模型类只有数据字段"(第 36 篇规范)是对象可稳定序列化的前提——一旦模型混入方法或资源对象,序列化结果将不再可靠。
三、class 与接口的类型安全解析
JSON.parse 返回类型是 object | null(实际语义接近 any),直接访问字段没有任何编译期保护。将 JSON 还原为 class 实例必须显式转换:
// 安全的反序列化:JSON -> AvDataSourceModel
function fromJson(json: string): AvDataSourceModel[] {
const raw = JSON.parse(json) as Array<{ currentSource?: string }>;
return raw.map((item) => new AvDataSourceModel(item.currentSource ?? ''));
}
这里的关键不是"强转",而是逐字段校验:as 只欺骗编译器,字段缺失时运行结果仍是 undefined。对于服务端可能缺字段的数据,应在构造函数层面兜底——项目模型构造函数中的 ?? 默认值(如 MSVDataModel 的 iconOnly ?? false)正是为此设计。反例是直接 JSON.parse(body) as CommentDataModel[] 后取 item.pic,一旦服务端未返回该字段,undefined 就会泄漏到 UI 层引发渲染异常。规范总结为:接口定义类型形状,class 提供构造兜底,两层配合实现类型安全。
四、复杂嵌套结构的解析
评论接口的响应是典型的嵌套结构:外层是评论数组,每条评论又含回复数组。解析时采用"逐层映射"策略:
// 嵌套 JSON -> CommentDataModel[](与 CommentViewModel 数据结构对应)
function parseComments(body: string): CommentDataModel[] {
const raw = JSON.parse(body) as Array<{
id?: number; name?: string; content?: string; likes?: string;
date?: string; location?: string;
replys?: Array<{ id?: number; name?: string; content?: string }>;
}>;
return raw.map((item) => new CommentDataModel(
item.id, undefined, item.name, item.likes, item.content, item.date, item.location,
item.replys?.map((r) => new ReplyDataModel(r.id, r.name, r.content))
));
}
注意 pic 字段(PixelMap | ResourceStr | DrawableDescriptor)在此处传 undefined——位图与资源引用属于运行时对象,无法从 JSON 还原。正确的做法是传输层只传资源标识(如 ic_user01),解析层(viewModel)再映射为 $r('app.media.ic_user01')。这正是"传输数据"与"展示数据"的分层:JSON 承载可序列化的基础类型,资源映射收敛在模型转换的边界上。
五、ArkTS 序列化限制与 @ohos.util 辅助
ArkTS 对运行时对象能力有严格约束,序列化相关限制尤其值得注意:
| 限制 | 说明 | 规避方案 |
|---|---|---|
禁止 eval/new Function | 无法动态执行代码 | 静态 map 转换 |
| 对象字面量受限 | 无 Record 任意键操作 | 用 class 承载结构 |
| 函数不可序列化 | stringify 静默丢弃方法 | 模型类只存数据 |
| 联合类型字段 | pic 等复杂类型无法直传 | 传输层用字符串标识 |
| 循环引用 | stringify 直接抛异常 | 模型禁止互相回指 |
@kit.ArkTS 的 @ohos.util 提供 JSON 之外的辅助工具,例如用 util.generateRandomUUID() 生成稳定的列表 key,替代字符串化大对象:
import { util } from '@kit.ArkTS';
const uuid = util.generateRandomUUID(); // 生成全局唯一 ID
// 可用作评论临时 id:new CommentDataModel(uuid, ...)
对于大对象序列化,项目还常借助 JSON.parse(JSON.stringify(obj)) 做深拷贝。要注意其性能开销随对象体积线性增长,且会丢失 undefined 与函数——频繁或大规模场景应改用显式字段拷贝。
六、本地 mock 与真实网络数据的转换
项目的 viewModel(如 CommentViewModel、WorksViewModel)目前以 $r() 资源直接构造模型。演进为网络数据源后,需在 viewModel 内完成"JSON ↔ 模型 ↔ 资源"三层转换,且保持对外接口(getCommentList() 等)不变:
// 统一入口:本地 mock 与网络数据都转换为 CommentDataModel[],UI 无感知
export class CommentViewModel {
private commentList: CommentDataModel[] = [];
async load(): Promise<void> {
// 1. 本地 mock(现有逻辑,作为兜底)
// 2. 网络 JSON(第 38 篇):parseComments(body) 得到模型
// 3. 资源映射:name/likes 由 JSON 字符串转为 $r() 或原样字符串
}
getCommentList(): CommentDataModel[] {
return this.commentList;
}
}
转换规范遵循三原则:传输层只用基础类型(string/number/boolean/数组),保证 JSON 可序列化;展示资源在 viewModel 层映射,模型字段与资源 API 解耦;解析失败降级到 mock 数据,任何 JSON 异常都不让 UI 空白。这样 JSON 解析逻辑与业务 UI 完全解耦,本地开发与线上环境切换只改 load() 内部实现。
七、总结与最佳实践
stringify的 key 用法适合小对象;大数据集改用id或util.generateRandomUUID()提升性能;JSON.parse返回值的as强转不提供运行时保护,必须逐字段校验并在构造函数兜底;- 嵌套结构采用"逐层 map 到模型类"的解析策略,与第 36 篇的嵌套模型一一对应;
- 位图、资源、函数等运行时对象不可 JSON 化,传输层用标识字符串,展示层再映射;
- ArkTS 禁止
eval/new Function,序列化只适用于纯数据 class,模型禁止循环引用; - 深拷贝用
JSON.parse(JSON.stringify(obj))有性能与语义损耗,大数据量改显式拷贝; - 转换逻辑统一收敛在 viewModel,mock 与网络数据通过同一接口对外,保证 UI 无感切换;
- 解析失败一律降级到 mock 或空列表,任何 JSON 异常不得抛到 UI 层。
更多推荐



所有评论(0)