DEV Community

HarmonyOS
HarmonyOS

Posted on

How to persistently store object-type data on a hard drive using PersistentStorage

Read the original article:How to persistently store object-type data on a hard drive using PersistentStorage

How to persistently store object-type data on a hard drive using PersistentStorage

Problem Description

PersistentStorage itself does not support the storage of object or array types. How can data be processed to meet the storage requirements and enable the storage of object and array types?

Background Knowledge

  • AppStorage: This is the application-wide UI state storage, which is bound to the application's process. It is created by the UI framework when the application starts, providing a central storage for the application's UI state properties.
  • PersistentStorage: This is the persistent storage of selected AppStorage properties, ensuring that these properties retain their values upon application restart, matching the values at the time of application closure.

Since PersistentStorage allows simple types such as number, string, boolean, and enum, it is considered to convert to string type for storage. JSON.stringify() can convert an object or value into a JSON string, so we can attempt to use this method to convert it into a string before storing it.

Troubleshooting Process

  1. Identify Issue: PersistentStorage only supports primitive types (number, string, boolean, enum). Objects/arrays cause errors.
  2. Serialize Data: Convert objects/arrays to JSON strings using JSON.stringify() before storage.
  3. Deserialize Data: On retrieval, parse strings back to objects/arrays with JSON.parse() (add error handling).
  4. Reactivity: Use @StorageLink for UI updates when stored data changes.

Analysis Conclusion

Solution: Serialize objects/arrays to JSON strings via JSON() for storage, then deserialize with JSON.parse() on retrieval.
Why It Works: JSON strings are primitive types supported by PersistentStorage.

Caveats:

  • Only works for JSON-serializable data (no methods/circular references).
  • Add try-catch for parsing errors and default values for empty storage.
  • Use @StorageLink to maintain UI reactivity.

Implementation:

// Storing
const data = [{ id: 1 }];
PersistentStorage.PersistProp('key', JSON.stringify(data));

// Retrieving
const storedData = JSON.parse(AppStorage.Get('key') || "[]");
Copy codeCopy code
Enter fullscreen mode Exit fullscreen mode

Solution

  1. For classes without methods, convert the array data into a string using JSON.stringify() and then store it. When reading, simply parse it using JSON.parse() and use it directly. The example code is as follows:
   class Student {  
     name: string  
     age: number  

     constructor(name: string, age: number) {  
       this.name = name  
       this.age = age  
     }  
   }  

   PersistentStorage.persistProp('studentArr', JSON.stringify([new Student('Tom', 16), new Student('Gina', 18)]));  

   @Entry  
   @Component  
   struct Index {  
     @State studentArr: Array<Student> = [];  
     @StorageLink('studentArr') @Watch('onStrChange') studentArrStr: string = '[]';  

     onStrChange() {  
       this.studentArr = JSON.parse(this.studentArrStr);  
     }  

     aboutToAppear(): void {  
       // The Watch event is not triggered during component initialization; the array is initialized through the aboutToAppear event.
       this.studentArr = JSON.parse(this.studentArrStr);  
     }  

     build() {  
       Column({ space: 8 }) {  
         ForEach(this.studentArr, (item: Student, index: number) => {  
           Column() {  
             Text(`Student Name: ${item.name}`)  
               .width('100%')  
             Text(`Student Age: ${item.age}`)  
               .width('100%')  
           }  
           .borderRadius(12)  
           .width('100%')  
           .backgroundColor(Color.White)  
           .padding(16)  
         }, (item: Student) => JSON.stringify(item))  
       }  
       .width('100%')  
       .height('100%')  
       .backgroundColor('#f1f3f5')  
       .padding(12)  
     }  
   }
Enter fullscreen mode Exit fullscreen mode
  1. For classes with method functions, it is necessary to first convert the string into a data array, then call the class constructor to create an object using each piece of data. This way, the created object will have a prototype chain and can call the corresponding methods. The reference code is as follows:
   class Student {  
     name: string  
     age: number  

     constructor(name: string, age: number) {  
       this.name = name  
       this.age = age  
     }  

     selfIntroduction() {  
       console.log(`My name is ${this.name} and I'm ${this.age} years old.`);  
     }  
   }  

   PersistentStorage.persistProp('studentArr', JSON.stringify([new Student('Tom', 16), new Student('Gina', 18)]));  

   @Entry  
   @Component  
   struct Index {  
     @State studentArr: Array<Student> = [];  
     @StorageLink('studentArr') @Watch('onStrChange') studentArrStr: string = '[]';  

     onStrChange() {  
       const dataArr: Array<Student> = JSON.parse(this.studentArrStr);  
       this.studentArr = dataArr.map((item: Student) => new Student(item.name, item.age));  
     }  

     aboutToAppear(): void {  
       // The Watch event is not triggered during component initialization; the array is initialized through the aboutToAppear event.
       const dataArr: Array<Student> = JSON.parse(this.studentArrStr);  
       this.studentArr = dataArr.map((item: Student) => new Student(item.name, item.age));  
     }  

     build() {  
       Column({ space: 8 }) {  
         ForEach(this.studentArr, (item: Student, index: number) => {  
           Column() {  
             // UI interface reference the code from the previous solution
             Button('Get Self-introduction')  
               .onClick(() => {  
                 item.selfIntroduction();  
               })  
           }  
           // Style reference the code from the previous solution 
         }, (item: Student) => JSON.stringify(item))  
       }  
       // Style reference the code from the previous solution
     }  
   }
Enter fullscreen mode Exit fullscreen mode

Verification Result

kbs--93e146b7ae4b49d2a6536e1261681db0-2c5b3.pngimage.png

Summary

To store an array of objects as data to the hard drive using PersistentStorage, you need to convert the objects into strings using JSON.stringify() before storing them. When using the data, handle it differently based on whether the class contains methods. For data classes without methods, simply parse them using JSON.parse() and then use them. For classes with methods, you need to call the class's constructor to create an object before the object can invoke the corresponding methods; otherwise, there is a high likelihood of application crashes.

Written by Emrecan Karakas

Top comments (0)