Flutter数据序列化与反序列化插件fling_pickle的使用
Flutter数据序列化与反序列化插件fling_pickle的使用
一个轻量级、小体积的Dart库用于序列化和反序列化(SerDes)。使用Pickles,你无需初始化复杂的系统或注解你的类——所有与SerDes相关的功能都显式地在代码中体现且非常直观。你可以将对象序列化为JSON以提高可读性,或者序列化为二进制以优化工件大小。
使用
虽然Pickles通常用于持久化对象状态或结构,但它们也可以被用作重量级的备忘录对象——例如,用于执行撤销/重做操作。相比经典的备忘录模式,它不允许其他类创建或查看备忘录本身,Pickles提供了基本接口。这种决策的代价是,“伪造”的备忘录(由被序列化的类以外的东西创建)可能被当作原版传递给恶意行为者(或在紧张的截止日期下工作的开发人员)。此外,由于Pickles必须作为一个通用的状态记录,它不如真正的备忘录类型安全。在将传统的备忘录模式替换为Pickles之前,请考虑这些成本。
然而,对于非敏感类,Pickles确实提供了一些作为轻量级序列化核心的好处。你可以快速设置一个类,其状态可以存储在文件或数据库中,稍后恢复,而无需任何混乱的初始化或魔法镜像。这意味着更好的性能和小体积。如果你后来决定需要更重的功能,这很容易提取出来——Pickles不需要对类本身进行任何更改或约定。
Pickles支持序列化到二进制和Dart的JSON表示。这意味着你可以无缝地使用许多核心库和插件,如dart:convert
或Firestore。详见下面的示例。
示例
最简单的用例涉及一个希望保存自身并稍后恢复的类。我们来处理一个计数器类:
class Counter {
int count;
Counter([final int start = 0]) : count = start;
int countUp() => count++;
}
完全集成
最基本的方法是我们只需要存储当前的count
以恢复对象的状态。我们可以根据想要的侵入性选择多种方法。我们从一个完全集成的例子开始:
class Counter implements Pickleable {
int count;
Counter([final int start = 0]) : count = start;
int countUp() => count++;
[@override](/user/override)
Pickle toPickle() => PickleBuilder().withInt('count', count).build();
static Counter fromPickle(final Pickle pickle) => Counter(pickle.readInt('count'));
}
三个主要的区别在于:
- 我们实现了
Pickleable
接口。 - 我们通过生成包含对象状态的Pickles来实现
toPickle()
。 - 我们通过生成基于Pickles值的Counter来实现
fromPickle()
。
这样我们就有了与Pickles的完全集成,并可以执行以下操作:
void main() {
var counter = Counter();
print(counter.countUp()); // 输出: 0
var pickle = counter.toPickle();
print(counter.countUp()); // 输出: 1
counter = Counter.fromPickle(pickle);
print(counter.countUp()); // 输出: 1
// 将我们的pickle写入文件。
// 我们可以根据需要使用同步或异步方法。
File('myCounter').writeAsBytesSync(Pickler().writeSync(pickle));
var pickleFromFile = Pickler().readSync(File('myCounter').readAsBytesSync());
counter = Counter.fromPickle(pickleFromFile);
print(counter.countUp()); // 输出: 1
}
此外,我们还完全支持嵌套Pickling。例如,一个使用Counter
生成版本号的类可以轻松地“pickle”自身及其内部Counter
的状态:
class Version implements Pickleable {
final Counter _counter;
Version() : _counter = Counter();
Version._(final this.counter);
String nextVersion() => 'v${_counter.countUp()}';
[@override](/user/override)
Pickle toPickle() => PickleBuilder().withPickleable('counter', _counter).build();
static Version fromPickle(final Pickle pickle) => Version._(pickle.readPickleable('counter'));
}
序列化和反序列化一个Version
与序列化和反序列化一个Counter
没有什么不同,包括将其保存到文件或其他任何形式的持久化方式:
void main() {
var version = Version();
print(version.nextVersion()); // 输出: v0
var pickle = version.toPickle();
print(version.nextVersion()); // 输出: v1
version = Version.fromPickle(pickle);
print(version.nextVersion()); // 输出: v1
// 保存到文件
File('MyVersion.pickle').writeAsBytes(BinaryPickler().writeSync(pickle));
// 或保存到数据库如Firestore
FirebaseFirestore.instance.collection('versions').add(JsonPickler.writeSync(pickle));
}
非侵入性集成
如果我们不想(或不能)修改我们想持久化的类,只要我们有恢复其状态所需的信息,我们仍然可以使用Pickles。以原始的Counter
类为例;我们可以通过编写几个类外的方法来支持基本的“pickle”:
class Counter {
int count;
Counter([final int start = 0]) : count = start;
int countUp() => count++;
}
// 在其他地方...
Pickle counterToPickle(final Counter counter) => PickleBuilder().withInt('start', counter.count).build();
Counter counterFromPickle(final Pickle pickle) => Counter(pickle.readInt('start'));
这样,我们将无法获得嵌套Pickles的良好集成支持,但我们仍然可以创建Pickles并恢复Counter
到以前的状态:
void main() {
var counter = Counter();
print(counter.countUp()); // 输出: 0
var pickle = counterToPickle(counter);
print(counter.countUp()); // 输出: 1
counter = counterFromPickle(pickle);
print(counter.countUp()); // 输出: 0
}
当然,我们仍然可以实现一个使用这些方法进行嵌套Pickling的Version
类:
class Version implements Pickleable {
final Counter _counter;
Version() : _counter = Counter();
Version._(final this.counter);
String nextVersion() => 'v${_counter.countUp()}';
[@override](/user/override)
Pickle toPickle() => PickleBuilder().withPickle('counter', counterToPickle(_counter)).build();
static Version fromPickle(final Pickle pickle) => Version._(pickleToCounter(pickle.readPickle('counter')));
}
根据项目需求选择正确的集成级别,并享受乐趣吧!
示例代码
import 'dart:io';
import 'package:fling_pickle/fling_pickle.dart';
// 对于完全集成,实现Pickleable接口并支持静态方法从Pickles创建实例(构造函数可以工作,但由于Dart不支持函数指针到构造函数,你会错过一些语法糖功能)。
class Color implements Pickleable {
final int r;
final int g;
final int b;
const Color(this.r, this.g, this.b);
[@override](/user/override)
String toString() {
return '($r, $g, $b)';
}
//----------start Added for SerDes----------//
static Color fromPickle(final Pickle pickle) => Color(
pickle.readInt('r'),
pickle.readInt('g'),
pickle.readInt('b'),
);
[@override](/user/override)
Pickle asPickle() {
return PickleBuilder()
.withInt('r', r)
.withInt('g', g)
.withInt('b', b)
.build();
}
//----------end Added for SerDes----------//
}
// 如果你希望保持类干净无序列化代码,可以单独实现转换器(只要你有创建实例所需的信息)。你也可以递归转换。
class Banana {
final String name;
final double age;
final Color favoriteColor;
final List<Color> potentialColors;
const Banana(
this.name,
this.age,
this.favoriteColor,
this.potentialColors,
);
[@override](/user/override)
String toString() {
return '$name is $age years old and likes the color $favoriteColor out of $potentialColors';
}
}
//----------start Added for SerDes----------//
Pickle pickleBanana(final Banana banana) => PickleBuilder()
.withString('name', banana.name)
.withDouble('age', banana.age)
.withPickleable('favoriteColor', banana.favoriteColor)
.withPickleables('potentialColors', banana.potentialColors)
.build();
Banana regurgitateBanana(final Pickle pickle) => Banana(
pickle.readString('name'),
pickle.readDouble('age'),
pickle.readPickleable('favoriteColor', Color.fromPickle),
pickle.readPickleables('potentialColors', Color.fromPickle));
//----------end Added for SerDes----------//
// 现在我们可以序列化和反序列化一个对象,即使它有嵌套的Pickleables。
void main() async {
final fruit = Banana(
'Yellow',
1.5,
Color(255, 255, 0),
[Color(1, 2, 3), Color(4, 5, 6)],
);
print('I am a Banana: $fruit');
// 要序列化对象,我们需要一个Pickler和对象的Pickles。
final pickler = BinaryPickler();
final pickle = pickleBanana(fruit);
// 我们可以根据需要使用异步或同步序列化。
final serializedPickle = pickler.writeSync(pickle);
// 我们可以对结果的序列化pickle做任何我们喜欢的事情。
final file = File('banana');
file.writeAsBytesSync(serializedPickle);
// 我们也可以异步或同步反序列化。
final deserializedPickle = pickler.readSync(file.readAsBytesSync());
// 反序列化后,我们回到了原来的对象。
final regurgitatedFruit = regurgitateBanana(deserializedPickle);
print('I am still a Banana: $regurgitatedFruit');
file.deleteSync();
}
更多关于Flutter数据序列化与反序列化插件fling_pickle的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter数据序列化与反序列化插件fling_pickle的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用fling_pickle
插件进行数据序列化与反序列化的示例代码。fling_pickle
是一个用于Flutter的数据序列化与反序列化的插件,类似于Python中的pickle
模块。
首先,确保你已经在pubspec.yaml
文件中添加了fling_pickle
依赖:
dependencies:
flutter:
sdk: flutter
fling_pickle: ^最新版本号 # 请替换为实际最新版本号
然后运行flutter pub get
来安装依赖。
接下来,创建一个简单的Flutter应用来演示如何使用fling_pickle
进行数据的序列化与反序列化。
import 'package:flutter/material.dart';
import 'package:fling_pickle/fling_pickle.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Fling Pickle Demo'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: PickleDemo(),
),
),
),
);
}
}
class PickleDemo extends StatefulWidget {
@override
_PickleDemoState createState() => _PickleDemoState();
}
class _PickleDemoState extends State<PickleDemo> {
String serializedData = '';
Map<String, dynamic> originalData = {
'name': 'Flutter',
'version': '3.0.0',
'isStable': true,
'features': ['Null Safety', 'Sound Null Safety'],
};
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Original Data:'),
Text('${originalData.toString()}'),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// 序列化数据
serializeData();
},
child: Text('Serialize Data'),
),
SizedBox(height: 20),
if (serializedData.isNotEmpty)
Text('Serialized Data:'),
if (serializedData.isNotEmpty)
Text('${serializedData}'),
SizedBox(height: 20),
if (serializedData.isNotEmpty)
ElevatedButton(
onPressed: () {
// 反序列化数据
deserializeData();
},
child: Text('Deserialize Data'),
),
SizedBox(height: 20),
if (deserializedData != null)
Text('Deserialized Data:'),
if (deserializedData != null)
Text('${deserializedData.toString()}'),
],
);
}
void serializeData() {
// 使用 FlingPickle 序列化数据
final pickle = FlingPickle();
pickle.encode(originalData).then((data) {
setState(() {
serializedData = String.fromCharCodes(data);
});
});
}
Map<String, dynamic> deserializedData;
void deserializeData() {
// 使用 FlingPickle 反序列化数据
final pickle = FlingPickle();
pickle.decode(Uint8List.fromList(serializedData.codeUnits)).then((result) {
setState(() {
deserializedData = result as Map<String, dynamic>;
});
});
}
}
代码解释:
- 依赖添加:在
pubspec.yaml
文件中添加fling_pickle
依赖。 - UI构建:创建了一个简单的Flutter应用,包含一个显示原始数据的
Text
控件和两个按钮,一个用于序列化数据,另一个用于反序列化数据。 - 序列化数据:在
serializeData
方法中,使用FlingPickle
的encode
方法将原始数据序列化为字节数组,并将其转换为字符串显示。 - 反序列化数据:在
deserializeData
方法中,使用FlingPickle
的decode
方法将之前序列化的数据反序列化为原始数据类型(这里是一个Map<String, dynamic>
)。
注意:由于fling_pickle
的具体API和实现可能会有所不同,因此请确保参考最新的官方文档和插件版本。如果插件的API有变化,请相应调整代码。