JSON反序列化最佳实践:OOP下不修改服务端的多接口响应合并方案
最优解决方案
核心思路是遵循依赖注入原则和单一职责原则,解除Picture模型序列化逻辑对全局用户数据的依赖,把用户匹配的逻辑放到上层业务层处理,有两种落地方式可选:
方案1:给fromJson方法传入用户映射参数(轻量场景首选)
直接给Picture的工厂构造方法新增用户ID映射参数,由上层调用方拉取完用户数据后传入,不需要额外新增类:
class Picture { final User user; final List<String> pictures; Picture(this.user, this.pictures); // 新增Map<int, User>参数,由外部传入已拉取的用户映射 factory Picture.fromJson(Map<String, dynamic> json, Map<int, User> userMap) { final int userId = json['userID']; final User? matchedUser = userMap[userId]; // 可根据业务需求处理用户找不到的场景:抛异常/过滤/占位 if (matchedUser == null) { throw ArgumentError('未找到ID为$userId的对应用户'); } return Picture( matchedUser, List<String>.from(json['urls']), ); } }
调用流程示例:
// 1. 先拉取用户接口,构造ID为key的用户映射(比遍历列表查找效率高) final userRes = await http.get(Uri.parse('/api/users?page=1')); final List<User> users = (jsonDecode(userRes.body)['users'] as List) .map((item) => User(item['id'], item['name'])) .toList(); final Map<int, User> userMap = { for (var u in users) u.id: u }; // 2. 拉取图片接口,传入userMap构造Picture实例 final picRes = await http.get(Uri.parse('/api/pictures?page=2')); final List<Picture> pictures = (jsonDecode(picRes.body)['pictures'] as List) .map((item) => Picture.fromJson(item, userMap)) .toList();
方案2:新增中间DTO层做转换(复杂场景首选)
如果希望保持模型类fromJson方法只接收JSON参数的约定,可新增专门的DTO类承接接口原始返回,再单独做数据合并,分层更清晰:
// 新增PictureDTO,只对应接口原始返回结构 class PictureDto { final int userId; final List<String> urls; PictureDto(this.userId, this.urls); factory PictureDto.fromJson(Map<String, dynamic> json) { return PictureDto( json['userID'], List<String>.from(json['urls']), ); } } // 单独封装合并逻辑,和模型类完全解耦 List<Picture> mergeUserAndPictureData(List<User> users, List<PictureDto> picDtos) { final Map<int, User> userMap = { for (var u in users) u.id: u }; return picDtos.map((dto) { final User user = userMap[dto.userId]!; // 自行处理空值场景 return Picture(user, dto.urls); }).toList(); }
优势说明
- 完全避免了全局单例的使用,所有依赖都由上层主动传入,符合OOP设计原则
- 不需要修改服务端返回结构,完全适配现有接口协议
- 匹配逻辑可灵活调整,适配不同业务场景的异常处理需求
内容的提问来源于stack exchange,提问作者Mahdi Bagheri
相关产品推荐
相关产品推荐

