Flutter如何解析含嵌套数组的JSON为自定义模型对象列表
从指定JSON生成Day对象列表的实现方法
原始数据与模型说明
你拿到的原始JSON结构如下:
{"-N42h_BKjWaEZRJyH8vS":[{"Data":"06/2022","imie":"John","DayList":[["7.50","12.00"],["00.00","00.00"],["8.00","16.00"],["00.00","00.00"],["00.00","00.00"],["7.50","15.00"],["00.00","00.00"],["00.00","00.00"],["00.00","00.00"],["8.00","15.25"],["00.00","00.00"],["00.00","00.00"],["7.50","15.00"],["00.00","00.00"]],"surname":"Smith"}]}
字段规则:
DayList的每个子列表对应当月1个工作日的上下班时间,索引i对应当月i+1日- 时间格式为
小时.分钟补零成两位小数,比如7.50代表7点30分,15.25代表15点15分,00.00代表当日无考勤 - 数据归属人员为
imie(名)+surname(姓)拼接的全名
已定义的Dart模型如下:
class Day { String name; DateTime dateTime; double startTime; double endTime; Day({ required this.name, required this.dateTime, required this.startTime, required this.endTime, }); }
具体解析实现
按结构逐层拆解解析即可,参考可直接运行的代码:
import 'dart:convert'; List<Day> buildDayListFromJson(String rawJson) { // 解码最外层JSON final Map<String, dynamic> outerMap = jsonDecode(rawJson); // 取最外层随机ID对应的考勤数据(该随机键是Firebase实时数据库默认生成的pushId) final List<dynamic> userMonthDataList = outerMap.values.first as List; final Map<String, dynamic> userMonthData = userMonthDataList.first as Map; // 解析人员姓名、年月基础信息 final String userName = '${userMonthData['imie']} ${userMonthData['surname']}'; final List<String> dateParts = (userMonthData['Data'] as String).split('/'); final int month = int.parse(dateParts[0]); final int year = int.parse(dateParts[1]); final List<dynamic> rawDayList = userMonthData['DayList'] as List; final List<Day> result = []; for (int index = 0; index < rawDayList.length; index++) { // 索引+1对应当月实际日期 final currentDate = DateTime(year, month, index + 1); final List<dynamic> timePair = rawDayList[index] as List; final double start = double.parse(timePair[0] as String); final double end = double.parse(timePair[1] as String); result.add(Day( name: userName, dateTime: currentDate, startTime: start, endTime: end, )); // 如果不需要保留无考勤的休息日,放开下面这行注释即可过滤 // if (start == 0 && end == 0) result.removeLast(); } return result; }
JSON结构优化方案
当前结构存在几个容易引发线上bug的设计缺陷,建议调整:
- 最外层用无意义的随机pushId作为主键,解析时只能取
values.first获取数据,后续如果同层新增其他字段很容易解析错位 - 字段名混用小语种和英文:
imie、Data是波兰语词汇,和其他英文字段混杂,不熟悉业务的开发者很难快速理解字段含义 - 日期靠数组索引映射:如果传输时漏了某一天的数据,后续所有日期都会对应错误,且单条数据本身不带日期信息,没法单独拆分使用
- 时间用小数字符串存储:
15.25代表15点15分的逻辑隐式依赖“分钟转两位小数”的规则,新人接手容易误解为15点25分,还存在浮点数精度风险 - 用
00.00作为无考勤的标记属于魔法值,如果后续出现0点上下班的排班场景会直接冲突,没有明确语义
优化后参考结构
{ "attendanceRecords": [ { "recordId": "-N42h_BKjWaEZRJyH8vS", "firstName": "John", "lastName": "Smith", "year": 2022, "month": 6, "workDays": [ { "date": "2022-06-01", "startMinutes": 450, "endMinutes": 720, "status": "normal" }, { "date": "2022-06-03", "startMinutes": 480, "endMinutes": 960, "status": "normal" } ] } ] }
优化点说明:
- 所有字段名统一为语义明确的英文驼峰命名,无歧义
- 时间直接存当天0点到时间点的分钟数整数,计算工时、格式化展示都不需要额外做小数转换,没有精度问题
- 每条考勤记录自带明确的ISO格式日期,不需要靠数组索引推断,漏传数据也不会引发全局错位
- 新增
status字段标记考勤状态(正常上班/休息/请假/缺勤),不需要用魔法值判断 - 最外层用明确的
attendanceRecords数组存储所有记录,不会因为随机键的问题解析出错
内容的提问来源于stack exchange,提问作者JBTG
相关产品推荐
相关产品推荐

