Flutter如何反序列化包含两个列表的JSON对象并基于共同ID关联数据?
Flutter如何反序列化包含两个列表的JSON对象并基于共同ID关联数据?
看起来你现在的代码是分开两次请求获取data和included列表,但这样不仅会重复发起网络请求浪费资源,还没解决核心的关联数据问题。你的JSON结构符合JSON:API规范,data里的帖子关联了用户、投票等实体,included里是这些关联实体的详情,我们可以通过一次请求拿到完整数据,再把两者关联起来。
下面是完整的解决方案:
第一步:创建对应的模型类
首先我们需要为JSON里的各个层级创建模型类,每个类包含fromJson构造函数来完成反序列化。这里我列出核心的几个类,你可以根据需要补充其他字段:
Attributes(帖子属性类)
class Attributes { final String message; final String visibility; final String createdAt; final String updatedAt; final dynamic metaInfo; final String type; final int likesCount; final int repliesCount; final List<dynamic> widgetsData; final bool isLiked; final bool isBookmarked; final bool isDeleted; final dynamic shareCount; Attributes({ required this.message, required this.visibility, required this.createdAt, required this.updatedAt, required this.metaInfo, required this.type, required this.likesCount, required this.repliesCount, required this.widgetsData, required this.isLiked, required this.isBookmarked, required this.isDeleted, required this.shareCount, }); factory Attributes.fromJson(Map<String, dynamic> json) { return Attributes( message: json['message'], visibility: json['visibility'], createdAt: json['created_at'], updatedAt: json['updated_at'], metaInfo: json['meta_info'], type: json['type'], likesCount: json['likes_count'], repliesCount: json['replies_count'], widgetsData: json['widgets_data'] ?? [], isLiked: json['is_liked'], isBookmarked: json['is_bookmarked'], isDeleted: json['is_deleted'], shareCount: json['share_count'], ); } }
关联关系相关类
处理帖子和其他实体的关联:
// 通用的关联数据结构 class RelatedData { final String id; final String type; RelatedData({required this.id, required this.type}); factory RelatedData.fromJson(Map<String, dynamic> json) { return RelatedData( id: json['id'], type: json['type'], ); } } // 帖子的关联关系总类 class Relationships { final RelatedData? user; final RelatedData? poll; final List<RelatedData> taggedUsers; // 其他关联字段可以根据JSON补充 final List<dynamic> taggedCompanies; final List<dynamic> taggedTopics; Relationships({ required this.user, required this.poll, required this.taggedUsers, required this.taggedCompanies, required this.taggedTopics, }); factory Relationships.fromJson(Map<String, dynamic> json) { return Relationships( user: json['user']['data'] != null ? RelatedData.fromJson(json['user']['data']) : null, poll: json['poll']['data'] != null ? RelatedData.fromJson(json['poll']['data']) : null, taggedUsers: (json['tagged_users']['data'] as List).map((e) => RelatedData.fromJson(e)).toList(), taggedCompanies: json['tagged_companies']['data'] ?? [], taggedTopics: json['tagged_topics']['data'] ?? [], ); } }
帖子主类(Datum)
这里我们额外添加字段来存储关联的实体详情:
class Datum { final String id; final String type; final Attributes attributes; final Relationships relationships; // 存储关联的实体详情 IncludedUser? postUser; IncludedPoll? postPoll; List<IncludedUser>? taggedUsersList; Datum({ required this.id, required this.type, required this.attributes, required this.relationships, this.postUser, this.postPoll, this.taggedUsersList, }); factory Datum.fromJson(Map<String, dynamic> json) { return Datum( id: json['id'], type: json['type'], attributes: Attributes.fromJson(json['attributes']), relationships: Relationships.fromJson(json['relationships']), ); } }
Included里的实体类
处理included列表中的用户和投票:
// 用户实体 class IncludedUser { final String id; final String type; final UserAttributes attributes; IncludedUser({ required this.id, required this.type, required this.attributes, }); factory IncludedUser.fromJson(Map<String, dynamic> json) { return IncludedUser( id: json['id'], type: json['type'], attributes: UserAttributes.fromJson(json['attributes']), ); } } class UserAttributes { final String displayName; final String username; final String dpThumbnail; final String marketView; UserAttributes({ required this.displayName, required this.username, required this.dpThumbnail, required this.marketView, }); factory UserAttributes.fromJson(Map<String, dynamic> json) { return UserAttributes( displayName: json['display_name'], username: json['username'], dpThumbnail: json['dp_thumbnail'], marketView: json['market_view'], ); } } // 投票实体 class IncludedPoll { final String id; final String type; final PollAttributes attributes; IncludedPoll({ required this.id, required this.type, required this.attributes, }); factory IncludedPoll.fromJson(Map<String, dynamic> json) { return IncludedPoll( id: json['id'], type: json['type'], attributes: PollAttributes.fromJson(json['attributes']), ); } } class PollAttributes { final String question; final List<String> options; final int voteCount; final String endsAt; final bool isExpired; PollAttributes({ required this.question, required this.options, required this.voteCount, required this.endsAt, required this.isExpired, }); factory PollAttributes.fromJson(Map<String, dynamic> json) { return PollAttributes( question: json['question'], options: List<String>.from(json['options']), voteCount: json['vote_count'], endsAt: json['ends_at'], isExpired: json['is_expired'], ); } }
第二步:编写统一的数据获取和关联方法
我们只需要发起一次网络请求,然后把included里的实体存入一个Map(用type:id作为key,方便快速查找),再遍历data列表,把关联的实体填充到帖子对象中:
Future<List<Datum>> getPostsWithAssociatedData() async { try { Response response = await _dio.get(url); final jsonData = response.data; // 1. 处理included列表,存入Map以便快速查找 final Map<String, dynamic> includedMap = {}; if (jsonData['included'] != null) { for (final item in jsonData['included']) { final key = "${item['type']}:${item['id']}"; if (item['type'] == 'user') { includedMap[key] = IncludedUser.fromJson(item); } else if (item['type'] == 'poll') { includedMap[key] = IncludedPoll.fromJson(item); } // 如果还有其他类型的实体,继续添加else if分支 } } // 2. 处理data列表,并关联included中的实体 final List<Datum> posts = []; for (final map in jsonData['data']) { final post = Datum.fromJson(map); // 关联帖子作者 if (post.relationships.user != null) { final userKey = "user:${post.relationships.user!.id}"; post.postUser = includedMap[userKey] as IncludedUser?; } // 关联帖子的投票 if (post.relationships.poll != null) { final pollKey = "poll:${post.relationships.poll!.id}"; post.postPoll = includedMap[pollKey] as IncludedPoll?; } // 关联被标记的用户列表 final taggedUsers = <IncludedUser>[]; for (final taggedUser in post.relationships.taggedUsers) { final userKey = "user:${taggedUser.id}"; final user = includedMap[userKey] as IncludedUser?; if (user != null) { taggedUsers.add(user); } } post.taggedUsersList = taggedUsers; posts.add(post); } return posts; } catch (e) { print("反序列化出错: $e"); return []; } }
第三步:使用数据
调用这个方法后,你拿到的每个Datum对象都已经包含了关联的用户、投票等详情,直接使用即可:
// 示例调用 getPostsWithAssociatedData().then((posts) { for (final post in posts) { print("帖子作者: ${post.postUser?.attributes.displayName}"); print("投票问题: ${post.postPoll?.attributes.question}"); print("被标记的用户: ${post.taggedUsersList?.map((u) => u.attributes.displayName).join(', ')}"); } });
一些优化建议
- 可以使用
json_serializable包自动生成模型类的序列化/反序列化代码,减少手动编写的工作量和出错概率 - 对于可能为null的字段,建议使用空安全的处理方式,避免运行时异常
- 如果
included里有更多类型的实体,只需要扩展includedMap的处理逻辑即可
备注:内容来源于stack exchange,提问作者who-aditya-nawandar
相关产品推荐
相关产品推荐

