You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 04:48:24