Json 解码 Sharedpreferences 返回 List<dynamic> 而不是 List<Contact>
Posted
技术标签:
【中文标题】Json 解码 Sharedpreferences 返回 List<dynamic> 而不是 List<Contact>【英文标题】:JsonDecoding Shared Preferences returning List<dynmaic> instead of List<Contact> 【发布时间】:2021-09-24 13:54:08 【问题描述】:我有一个列表 contacts
存储 Contact
这是从 contact_services
包获取的类型
List<Contact> contacts = [];
我使用SharedPrefrences
存储列表,首先jsonEncode
它然后保存它
void saveContacts() async
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('list', jsonEncode(contacts));
但是当我尝试加载列表时,它返回异常type 'List<dynamic>' is not a subtype of type 'List<Contact>'
void loadList() async
SharedPreferences prefs = await SharedPreferences.getInstance();
contacts = await jsonDecode(prefs.getString('list'));
更新代码以突出显示更改:
这是整个saveContacts
函数:
void saveContacts() async
SharedPreferences prefs = await SharedPreferences.getInstance();
var json = jsonEncode(contacts, toEncodable: (e) => e.toMap());
await prefs.setString('list', jsonEncode(json));
但我收到错误消息:The method 'toMap' isn't defined for the type 'Object'.
contacts只是一个存储Contact
类型的List
List<Contact> contact;
最初contact
存储在一个单独的文件夹(全局)中以便于访问,但这并不影响jsonEncode
的结果
【问题讨论】:
这能回答你的问题吗? How to Deserialize a list of objects from json in flutter 否,因为问题不在于访问 josn 字符串中的信息,而是如何将其从字符串转换回联系人类型。 -@ישו אוהב אותך 【参考方案1】:您需要转换为 Map<String, dynamic>
,然后将 json 转换为您的 Contact
。这是一个将Contact
列表转换为 json 文本并作为联系人列表再次读取的工作示例。 (转到下面代码中的parseContacts
函数)。
import 'dart:convert';
class Contact
final String name;
final String phone;
Contact(this.name, this.phone);
Contact.fromJson(Map<String, dynamic> json)
: name = json['name'],
phone = json['phone'];
Map<String, dynamic> toJson() =>
'name': name,
'phone': phone,
;
void main()
List<Contact> contacts = [];
for (int i = 0; i < 10; i++)
contacts.add(Contact("name$i", i.toString()));
var jsonText = jsonEncode(contacts);
print(jsonText);
var contactsFromJson = parseContacts(jsonText);
for (var item in contactsFromJson)
print(item.name);
List<Contact> parseContacts(String jsonText)
final parsed = jsonDecode(jsonText).cast<Map<String, dynamic>>();
return parsed.map<Contact>((json) => Contact.fromJson(json)).toList();
更多详情,您可以阅读https://flutter.dev/docs/cookbook/networking/background-parsing#complete-example的示例
【讨论】:
嘿,Contact
是从插件contacts_service
获取的类型,因此它不是一个josn 文件,您不能使用fromJson
从中提取数据。顺便说一句,Contact
只包含一个像这样的电话号码的字符串:+123456789
,但我必须将其转换为Contact
才能在插件的其余部分中使用它。 -@ישו אוהב אותך
@AnanSaadi:你是说这个pub.dev/packages/contacts_service 包吗?您可以尝试以下代码进行编码:var json = jsonEncode(contacts, toEncodable: (e) => e.toMap());
然后在parseContacts
中将Contact.fromJson(json)).toList();
更改为Contact.fromMap(json)).toList();
,您需要使用库中的Contact
对象。见相关代码:github.com/lukasgit/flutter_contacts/blob/master/lib/…
我收到错误消息:error: The method 'toMap' isn't defined for the type 'Object'.
- @ישו אוהב אותך【参考方案2】:
更新:
我原本以为问题可以通过cast
ing 解决,但是潜入json.dart
,我发现了以下cmets:
json.encode
/jsonEncode
/// If value contains objects that are not directly encodable to a JSON
/// string (a value that is not a number, boolean, string, null, list or a map
/// with string keys), the [toEncodable] function is used to convert it to an
/// object that must be directly encodable.
///
/// If [toEncodable] is omitted, it defaults to a function that returns the
/// result of calling `.toJson()` on the unencodable object.
JsonEncoder.convert
内部由jsonEncode
/json.encode
使用:
/// Directly serializable values are [num], [String], [bool], and [Null], as
/// well as some [List] and [Map] values. For [List], the elements must all be
/// serializable. For [Map], the keys must be [String] and the values must be
/// serializable.
///
/// If a value of any other type is attempted to be serialized, the
/// `toEncodable` function provided in the constructor is called with the value
/// as argument. The result, which must be a directly serializable value, is
/// serialized instead of the original value.
TLDR;如果value
参数包含一个对象,如果该类没有toJson
实现,我们还必须提供toEncodable
。而且由于在您的情况下您无法为该类提供toJson
实现,因此正确编码对象的唯一方法是提供toEncodable
。我已经实现了一个示例供您理解:
import 'dart:convert';
class Class
String name;
Class(this.name);
@override
String toString() => name;
void main()
final list = <Class>[
Class('a'),
Class('b'),
Class('c'),
];
final encoded = json.encode(list, toEncodable: (c)
if (c is Class)
return 'name': c.name;
);
print(encoded);
// Save and retreive `encoded`
print((json.decode(encoded) as List).map((map)
if (map.containsKey('name'))
return Class(map['name']);
));
附录:不要提供toEncodable
的自定义实现并从json 创建Contact
,而是尝试已经定义的toMap
和fromMap
。
原答案:
您可以在List
上使用cast
方法转换列表类型。
所以首先使用as
将解码后的数据转换为List
,然后在其上应用cast
以获得所需的类型。
contacts = await (jsonDecode(prefs.getString('list')) as List).cast<Contact>();
【讨论】:
我遇到了一个异常type 'String' is not a subtype of type 'Contact' in type cast
-@happy_san
你能把你更新的代码提供给我吗?
我完全复制了您提供的代码,我认为问题在于,一旦 json 字符串被解码,它就会作为行字符串而不是列表出现,因此 cast 方法不知道如何将其转换为 Contact
类型 - @happy_san
@AnanSaadi 你试过我的解决方案了吗?
嘿,我很忙,所以我没有检查代码,但我试过了,但我得到了一个错误:error: The method 'toMap' isn't defined for the type 'Object'.
@happy_san以上是关于Json 解码 Sharedpreferences 返回 List<dynamic> 而不是 List<Contact>的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的 LinkedHashMap 被错误地转换为 JSON 并保存在 SharedPreferences 中?
Flutter 使用 SharedPreferences 和 Json 来存储对象
如何在flutter中使用mysql数据库(或json)中的sharedpreferences键选择以列出最喜欢的记录?