Flutter从Hive读取数据时触发JSON解析错误:Unexpected character的修复方案咨询
Flutter从Hive读取数据时触发JSON解析错误:Unexpected character的修复方案咨询
Hey,我来帮你分析下这个问题的根源,然后给出两种解决办法:
错误原因解析
你遇到的Unexpected character错误,是因为你在尝试用jsonDecode解析一个非标准JSON格式的字符串。你打印出来的{id: 2, network: MTN, discount: 2, logo: mtncard.png}其实是Dart中Map对象的字符串表示,而不是符合规范的JSON(JSON要求所有键必须用双引号包裹,比如{"id": "2", "network": "MTN"...}),所以jsonDecode自然会报错。
解决方案一:快速修复(直接使用Hive读取的Map对象)
既然你之前是把Map类型的数据存入Hive的,那读取出来的本身就是Map对象,根本不需要用jsonDecode去解析。直接修改你FutureBuilder里的代码即可:
// 去掉原来的jsonDecode行,直接转换类型 final Map<String, dynamic> decodedData = snapshot.data[index] as Map<String, dynamic>; final itemData = Provider.fromJson(decodedData);
这样就能跳过错误的JSON解析步骤,直接用你已经写好的fromJson方法把Map转成Provider对象。
解决方案二:推荐方案(使用Hive TypeAdapter存储自定义对象)
上面的方法只是临时修复,更规范的做法是让Hive直接存储和读取你的Provider自定义对象,不需要手动处理Map和JSON的转换,步骤如下:
- 添加依赖
在pubspec.yaml里补充Hive相关的开发依赖:
dependencies: hive: ^2.2.3 hive_flutter: ^1.1.0 dev_dependencies: build_runner: ^2.1.7 hive_generator: ^1.1.5
- 修改Provider类,添加Hive注解
给你的Provider类加上Hive的序列化注解,并生成对应的Adapter:
import 'package:hive/hive.dart'; // 这行是用来指定生成的Adapter文件,后面会自动生成 part 'provider.g.dart'; // 每个自定义类型需要唯一的typeId,这里用0即可 @HiveType(typeId: 0) class Provider { // 每个字段用HiveField标记,索引从0开始递增 @HiveField(0) final String id; @HiveField(1) final String network; @HiveField(2) final int discount; @HiveField(3) final String logo; Provider({ required this.id, required this.network, required this.discount, required this.logo, }); }
- 生成TypeAdapter文件
在终端运行以下命令,自动生成provider.g.dart文件:
flutter packages pub run build_runner build
- 在main函数中注册Adapter
初始化Hive的时候注册生成的Adapter:
import 'package:hive_flutter/hive_flutter.dart'; import 'provider.dart'; // 导入你的Provider类 void main() async { await Hive.initFlutter(); // 注册生成的ProviderAdapter Hive.registerAdapter(ProviderAdapter()); // 打开专门存储Provider类型的box await Hive.openBox<Provider>('my_db'); runApp(MyApp()); }
- 修改存储和读取逻辑
- 存储的时候直接存
Provider对象:
final box = Hive.box<Provider>('my_db'); await box.add(Provider(id: "2", network: "MTN", discount: 2, logo: "mtncard.png"));
- 修改读取数据的函数:
Future<List<Provider>> LoadData() async { final box = Hive.box<Provider>('my_db'); return box.values.toList(); }
- 最后修改FutureBuilder,直接使用读取到的
Provider对象:
FutureBuilder<List<Provider>>( future: LoadData(), builder: (BuildContext context, AsyncSnapshot<List<Provider>> snapshot) { if (!snapshot.hasData) { return const Center(child: Text('loading')); } else { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (BuildContext context, int index) { // 直接获取Provider对象,无需转换 final itemData = snapshot.data![index]; return ListTile( title: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(width: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( itemData.discount.toString(), style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 20, color: Colors.green, ), ), const Icon(Icons.more_horiz, color: Colors.blue), ], ), ], ), ); }, ); } }, ),
这种方法不仅能彻底解决你的解析错误,还能让数据存储和读取的逻辑更清晰、更安全,避免手动类型转换带来的潜在问题。
备注:内容来源于stack exchange,提问作者Monday P Godstime
相关产品推荐
相关产品推荐

