在HarmonyOs日记应用项目中Preferences 只能存储基本类型string、number、boolean而日记数据是复杂的对象数组。为了在 Preferences 中存储对象数据项目采用了 JSON 序列化方案——将对象数组转换为 JSON 字符串存储读取时再反序列化。本篇将深入解析这一过程。序列化与反序列化概念序列化Serialization 对象 → 字符串 { id: 1, title: 日记 } → {id:1,title:日记} 反序列化Deserialization 字符串 → 对象 {id:1,title:日记} → { id: 1, title: 日记 }日记项目中的序列化流程写入流程 ┌──────────┐ JSON.stringify() ┌──────────┐ put flush ┌──────────┐ │ Array of │ ─────────────────────→ │ JSON │ ────────────────→ │ Preferences │ │DiaryItem │ │ 字符串 │ │ 文件 │ └──────────┘ └──────────┘ └──────────┘ 读取流程 ┌──────────┘ JSON.parse() ┌──────────┐ get 默认值 ┌──────────┐ │ Array of │ ←───────────────── │ JSON │ ←──────────────── │ Preferences │ │DiaryItem │ │ 字符串 │ │ 文件 │ └──────────┘ └──────────┘ └──────────┘序列化实现存储时序列化constSTORE_KEYdiary_list;asyncsaveDiary(diary:DiaryItem):Promiseboolean{// ... 获取列表、修改 ...// 序列化ArrayDiaryItem → JSON 字符串constjsonStrJSON.stringify(list);// 存储到 Preferencesawaitthis.store.put(STORE_KEY,jsonStr);awaitthis.store.flush();returntrue;}JSON.stringify 输出示例[{id:diary_1721856000000_42,title:美好的一天,content:今天去公园散步天气很好。路边的花都开了空气清新。,date:2026-07-24,createTime:1721856000000,updateTime:1721856000000,mood:happy,weather:sunny},{id:diary_1721855000000_15,title:工作日记,content:今天完成了项目报告。,date:2026-07-23,createTime:1721855000000,updateTime:1721855000000,mood:calm,weather:cloudy}]读取时反序列化asyncgetAllDiaries():PromiseDiaryItem[]{if(!this.store){return[];}try{// 1. 从 Preferences 读取字符串默认值为 []constrawawaitthis.store.get(STORE_KEY,[])asstring;// 2. 反序列化JSON 字符串 → ArrayDiaryItemconstlist:ArrayDiaryItemJSON.parse(raw);// 3. 排序后返回list.sort((a:DiaryItem,b:DiaryItem)b.updateTime-a.updateTime);returnlist;}catch(e){console.error(getAllDiaries error: JSON.stringify(e));return[];}}默认值的重要性constrawawaitthis.store.get(STORE_KEY,[])asstring;[]默认值的作用首次使用应用时 Preferences 文件中不存在 diary_list 键 ↓ store.get(diary_list, []) 返回默认值 [] ↓ JSON.parse([]) 返回空数组 [] ↓ list.sort(...) → 空数组排序还是空数组 ↓ 返回 [] → UI 显示空状态 还没有日记哦如果没有默认值// 假设不提供默认值constrawawaitthis.store.get(STORE_KEY)asstring;// raw 可能是 undefinedJSON.parse(undefined)// → 报错SyntaxErrorJSON 序列化的注意事项1. 方法不会被序列化classDiaryItem{id:string;title:string;// 这个方法不会被序列化getTitle():string{returnthis.title;}}constitemnewDiaryItem();item.title测试;constjsonJSON.stringify(item);// json {id:,title:测试}// getTitle 方法不在 JSON 中日记项目的 DiaryItem 没有自定义方法只有数据字段所以序列化是完整的。2. 反序列化后丢失类型信息// 序列化constitemnewDiaryItem(1,标题,内容,2026-07-24);constjsonJSON.stringify(item);// 反序列化constparsedJSON.parse(json);// parsed 不是 DiaryItem 类的实例// parsed instanceof DiaryItem → false// 但 parsed 的属性结构与 DiaryItem 一致在日记项目中这不是问题因为代码只访问属性不调用方法// 只访问属性所以不需要类型信息list.sort((a:DiaryItem,b:DiaryItem)b.updateTime-a.updateTime);// a 和 b 实际上是普通对象但有 updateTime 属性3. 特殊字符处理constdiarynewDiaryItem();diary.title包含引号的标题;diary.content包含\n换行符的内容;constjsonJSON.stringify(diary);// JSON 自动处理特殊字符// {title:包含\引号\的标题,content:包含\n换行符的内容}constparsedJSON.parse(json);// parsed.title 包含引号的标题 ✅ 正确还原// parsed.content 包含\n换行符的内容 ✅ 正确还原JSON.stringify/parse 自动处理了转义字符无需手动处理。数据一致性保障读-改-写的原子性asyncsaveDiary(diary:DiaryItem):Promiseboolean{try{// 1. 读取全部数据constlistawaitthis.getAllDiaries();// 2. 修改更新或新增// ...// 3. 写回全部数据awaitthis.store.put(STORE_KEY,JSON.stringify(list));awaitthis.store.flush();returntrue;}catch(e){console.error(saveDiary error: JSON.stringify(e));returnfalse;}}原子性分析时间 → T1: 读取 [A, B, C] T2: 修改 → [A, B, C, D] T3: 写入 [A, B, C, D] T4: flush → 持久化 如果在 T1-T3 之间有另一个操作也读取了 [A, B, C] - 两个操作都基于 [A, B, C] 修改 - 最后写入的会覆盖先写入的 - 可能丢失数据日记项目的应对日记操作频率低用户手动操作不会出现高并发场景不需要加锁机制flush 的保障awaitthis.store.put(STORE_KEY,JSON.stringify(list));// 写入内存缓存awaitthis.store.flush();// 持久化到文件flush确保数据从内存缓存写入文件系统。在flush之前数据只存在于内存中应用崩溃会丢失。错误处理与数据恢复JSON 解析错误try{constrawawaitthis.store.get(STORE_KEY,[])asstring;constlist:ArrayDiaryItemJSON.parse(raw);// ...}catch(e){console.error(getAllDiaries error: JSON.stringify(e));return[];// 解析失败时返回空数组}可能触发解析错误的场景数据文件损坏版本升级导致数据格式不兼容手动修改了存储文件降级策略返回空数组用户看到空列表虽然丢失了旧数据但应用不会崩溃。序列化中的类型转换// DiaryItem 类定义exportclassDiaryItem{id:string;title:string;content:string;date:string;createTime:number;updateTime:number;mood:string;weather:string;}反序列化后的类型验证constrawawaitthis.store.get(STORE_KEY,[])asstring;constlist:ArrayDiaryItemJSON.parse(raw);// JSON.parse 返回的对象类型声明为 DiaryItem[]// 但实际是普通对象属性类型由 JSON 内容决定// 如果 JSON 中的 createTime 是字符串而非数字排序可能出错list.sort((a:DiaryItem,b:DiaryItem)b.updateTime-a.updateTime);在正常流程中不会出问题因为JSON.stringify会保持原始类型。存储格式设计当前格式单 Key 存全部constSTORE_KEYdiary_list;// 一个 key 存储所有日记awaitthis.store.put(STORE_KEY,JSON.stringify(allDiaries));优点实现简单一次读取获取全部数据排序在内存中完成缺点修改一条需要重写全部数据量大时性能下降替代方案每条日记一个 Key// 理论上的替代方案constdiaryKeydiary_diary.id;awaitthis.store.put(diaryKey,JSON.stringify(diary));// 维护一个 ID 列表awaitthis.store.put(diary_ids,JSON.stringify([diary_1,diary_2,...]));对比方案读取全部修改单条删除单条复杂度单Key存全部1次读取1读1写1读1写低每条独立KeyN1次读取1写2读1写中对于日记项目数据量小单 Key 方案更简洁高效。小结JSON 序列化是日记项目数据持久化的关键技术序列化存储JSON.stringify将对象数组转为字符串反序列化读取JSON.parse将字符串还原为对象数组默认值保障首次使用返回空数组避免解析错误错误恢复解析失败时安全降级返回空数组原子性写入put flush 确保数据完整持久化这种方案简单、可靠完美匹配日记应用的数据存储需求。