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

Dart JSON解析空值检查错误解决及Model创建最佳实践

问题:Dart JSON解析空值错误及Model类最佳实践

我在Dart项目中进行JSON数据解析时遇到以下问题:

一、相关代码与数据

1. 待解析的JSON数据

{
 "data": {
  "getJobs": [
  {
    "id": "124",
    "title": "another",
    "addressId": null,
    "status": "OPEN",
    "companyAddress": null,
    "industry": "IT",
    "jobType": "PART_TIME",
    "salaryMin": 10000,
    "salaryMax": 20000,
    "deadline": "2021-04-19T00:00:00.000Z",
    "isFeatured": false,
    "description": "test check"
   }
  ]
 }
}

2. quicktype生成的Model类

import 'dart:convert';

GetJobsResponseModel getJobsResponseModelFromJson(String str) => 
GetJobsResponseModel.fromJson(json.decode(str));

String getJobsResponseModelToJson(GetJobsResponseModel data) => 
json.encode(data.toJson());

class GetJobsResponseModel {
Data? data;

GetJobsResponseModel({
    this.data,
});

factory GetJobsResponseModel.fromJson(Map<String, dynamic> json) => 
 GetJobsResponseModel(
    data: json["data"] == null ? null : Data.fromJson(json["data"]),
);

Map<String, dynamic> toJson() => {
    "data": data?.toJson(),
};
}

class Data {
List<GetJob>? getJobs;

Data({
    this.getJobs,
});

factory Data.fromJson(Map<String, dynamic> json) => Data(
    getJobs: json["getJobs"] == null ? [] : List<GetJob>.from(json["getJobs"]!.map((x) 
=> GetJob.fromJson(x))),
);

Map<String, dynamic> toJson() => {
    "getJobs": getJobs == null ? [] : List<dynamic>.from(getJobs!.map((x) => 
x.toJson())),
};
}

class GetJob {
String? id;
String? title;
dynamic addressId;
String? status;
dynamic companyAddress;
String? industry;
String? jobType;
int? salaryMin;
int? salaryMax;
DateTime? deadline;
bool? isFeatured;
String? description;

GetJob({
    this.id,
    this.title,
    this.addressId,
    this.status,
    this.companyAddress,
    this.industry,
    this.jobType,
    this.salaryMin,
    this.salaryMax,
    this.deadline,
    this.isFeatured,
    this.description,
});

factory GetJob.fromJson(Map<String, dynamic> json) => GetJob(
    id: json["id"],
    title: json["title"],
    addressId: json["addressId"],
    status: json["status"],
    companyAddress: json["companyAddress"],
    industry: json["industry"],
    jobType: json["jobType"],
    salaryMin: json["salaryMin"],
    salaryMax: json["salaryMax"],
    deadline: json["deadline"] == null ? null : DateTime.parse(json["deadline"]),
    isFeatured: json["isFeatured"],
    description: json["description"],
);

Map<String, dynamic> toJson() => {
    "id": id,
    "title": title,
    "addressId": addressId,
    "status": status,
    "companyAddress": companyAddress,
    "industry": industry,
    "jobType": jobType,
    "salaryMin": salaryMin,
    "salaryMax": salaryMax,
    "deadline": deadline?.toIso8601String(),
    "isFeatured": isFeatured,
    "description": description,
   };
 }

3. Service层代码

Future<List<GetJob>> getAllJobs(){
  try{
    // 省略请求逻辑
    final data = result.data?['data']['getJobs'] as List;
    final List<GetJob> jobs = data.map((job) => GetJob.fromJson(job)).toList();
    return jobs;
  } catch(e){
    print('Error fetching data: $e');
    rethrow; 
  }
}

二、遇到的问题

运行时抛出错误:Error fetching data: Null check operator used on a null value,返回空列表,UI无法展示id、title等字段。需要解决这个错误,同时想了解创建Model类的最佳方式。


解决方案

一、解决空值错误

错误根源在于Service层的链式取值result.data?['data']['getJobs']:虽然result.data用了可空访问符,但后续的['data']和['getJobs']若前置节点为null,直接取值会触发空值错误,且强制转as List会在数据为null时崩溃。

方案1:逐层空值判断

修改Service层代码,对每个节点做空值校验:

Future<List<GetJob>> getAllJobs() async {
  try{
    // 省略请求逻辑,确保result是请求返回的响应对象
    if (result.data == null) {
      return [];
    }
    final responseData = result.data as Map<String, dynamic>;
    final dataNode = responseData['data'] as Map<String, dynamic>?;
    if (dataNode == null) {
      return [];
    }
    final getJobsList = dataNode['getJobs'] as List?;
    if (getJobsList == null) {
      return [];
    }
    final List<GetJob> jobs = getJobsList.map((job) => GetJob.fromJson(job as Map<String, dynamic>)).toList();
    return jobs;
  } catch(e){
    print('Error fetching data: $e');
    rethrow; 
  }
}

方案2:利用生成的Model类解析

直接用quicktype生成的Model类处理整个JSON,避免手动链式取值,更安全简洁:

Future<List<GetJob>> getAllJobs() async {
  try{
    // 假设responseBody是接口返回的原始JSON字符串
    final responseModel = getJobsResponseModelFromJson(responseBody);
    return responseModel.data?.getJobs ?? [];
  } catch(e){
    print('Error fetching data: $e');
    rethrow; 
  }
}

二、创建Model类的最佳方式

1. 优先使用代码生成工具

  • quicktype:当前使用的工具,支持多语言,能快速生成带空值处理的Model类,适合快速开发。生成时可配置空安全选项,确保代码符合Dart空安全规范。
  • json_serializable:Dart官方推荐的代码生成库,配合build_runner使用,高度可定制,支持自定义解析逻辑,适合大型项目。使用步骤:
    1. 添加依赖到pubspec.yaml:
      dependencies:
        json_annotation: ^4.8.1
      dev_dependencies:
        build_runner: ^2.4.4
        json_serializable: ^6.7.0
      
    2. 编写实体类并添加注解:
      import 'package:json_annotation/json_annotation.dart';
      
      part 'get_job.g.dart';
      
      @JsonSerializable()
      class GetJob {
        final String? id;
        final String? title;
        final dynamic addressId;
        // 其他字段...
      
        GetJob({this.id, this.title, this.addressId, /* 其他参数 */});
      
        factory GetJob.fromJson(Map<String, dynamic> json) => _$GetJobFromJson(json);
        Map<String, dynamic> toJson() => _$GetJobToJson(this);
      }
      
    3. 运行命令生成代码:dart run build_runner build

2. 手动编写Model类(简单场景)

若JSON结构简单,可手动编写Model类,但需严格遵循Dart空安全规范,对可选字段用?标记,解析时做空值判断。

3. 核心注意事项

  • 避免不必要的!强制非空操作,严格处理空值;
  • 对日期、枚举等特殊类型,添加自定义解析逻辑(如json_serializable可用@JsonKey注解处理日期);
  • 保持Model类单一职责,仅负责数据解析与序列化。

内容的提问来源于stack exchange,提问作者Dhiraj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 15:34:56