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

Flutter处理GraphQL响应时嵌套数据类空值转换异常求助

解决GraphQL响应中嵌套类字段为Null时的类型转换异常

问题概述

处理GraphQL响应的User数据类时,当LegalGuardian、Birthdate等嵌套类字段为null时,抛出以下异常:

[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'Map<String, dynamic>'

问题根源

手动实现的fromJson方法中,直接调用嵌套类的fromJson方法但未做null判断:

  • User类的fromJson里,birthdate: Birthdate.fromJson(data['birthdate'])这类代码,若data['birthdate']为null,会将null传入要求Map<String, dynamic>类型的Birthdate.fromJson,触发类型转换异常。
  • LegalGuardian类的birthdate处理中,json.decode(data['birthdate'])未判断null,若data['birthdate']为null,json.decode(null)会引发错误;同时若GraphQL返回的birthdate本身是Map而非JSON字符串,json.decode属于多余操作。

解决方案

方案1:手动修复fromJson方法,添加Null判断

修改User类的fromJson方法,对嵌套字段添加null校验:

factory User.fromJson(Map<String, dynamic> data) {
  return User(
    sub: data['sub'],
    email: data['email'],
    username: data['username'],
    roles: data['roles'],
    name: data['name'],
    firstName: data['firstName'],
    lastName: data['lastName'],
    newsletterSubscription: data['newsletterSubscription'],
    avatarUrl: data['avatarUrl'],
    backgroundUrl: data['backgroundUrl'],
    emailVerified: data['emailVerified'],
    // 添加null判断,仅当字段不为null时调用fromJson
    birthdate: data['birthdate'] != null 
        ? Birthdate.fromJson(data['birthdate'] as Map<String, dynamic>) 
        : null,
    address: data['address'] != null 
        ? Address.fromJson(data['address'] as Map<String, dynamic>) 
        : null,
    legalGuardian: data['legalGuardian'] != null 
        ? LegalGuardian.fromJson(data['legalGuardian'] as Map<String, dynamic>) 
        : null,
    externalSource: data['externalSource'],
  );
}

修改LegalGuardian类的fromJson方法,修复birthdate的null处理:

factory LegalGuardian.fromJson(Map<String, dynamic> data) {
  return LegalGuardian(
    email: data['email'],
    firstName: data['firstName'],
    lastName: data['lastName'],
    phone: data['phone'],
    // 若birthdate是JSON字符串,先判断null再解码;若为Map则直接传入
    birthdate: data['birthdate'] != null 
        ? Birthdate.fromJson(json.decode(data['birthdate']) as Map<String, dynamic>) 
        : null,
    // 若GraphQL返回的birthdate是Map类型,替换为下面这行:
    // birthdate: data['birthdate'] != null ? Birthdate.fromJson(data['birthdate'] as Map<String, dynamic>) : null,
    consent: data['consent'],
  );
}

方案2:使用json_annotation自动生成序列化代码(推荐)

手动编写fromJson和toJson容易出错,推荐依赖json_annotation自动生成代码,自动处理null场景:

  1. 简化User类代码,保留注解和构造函数,删除手动实现的fromJson和toJson:
import 'package:json_annotation/json_annotation.dart';
import '/src/dataclasses/user_address.dart';
import '/src/dataclasses/user_birthdate.dart';
import '/src/dataclasses/user_legal_guardian.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  String? sub;
  String? email;
  String? username;
  List<dynamic>? roles;
  String? name;
  String? firstName;
  String? lastName;
  bool? newsletterSubscription;
  String? avatarUrl;
  String? backgroundUrl;
  bool? emailVerified;
  Birthdate? birthdate;
  Address? address;
  LegalGuardian? legalGuardian;
  String? externalSource;

  User({
    this.email,
    this.username,
    this.roles,
    this.name,
    this.firstName,
    this.lastName,
    this.newsletterSubscription,
    this.avatarUrl,
    this.backgroundUrl,
    this.emailVerified,
    this.sub,
    this.birthdate,
    this.address,
    this.legalGuardian,
    this.externalSource,
  });

  // 自动生成的fromJson和toJson
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}
  1. 简化LegalGuardian类代码:
import 'dart:convert';
import 'package:json_annotation/json_annotation.dart';
import '/src/dataclasses/user_birthdate.dart';

part 'user_legal_guardian.g.dart';

@JsonSerializable()
class LegalGuardian {
  String? email;
  String? firstName;
  String? lastName;
  String? phone;
  Birthdate? birthdate;
  bool? consent;

  LegalGuardian({
    this.email,
    this.firstName,
    this.lastName,
    this.phone,
    this.birthdate,
    this.consent,
  });

  LegalGuardian.empty();

  // 自动生成的fromJson和toJson
  factory LegalGuardian.fromJson(Map<String, dynamic> json) => _$LegalGuardianFromJson(json);
  Map<String, dynamic> toJson() => _$LegalGuardianToJson(this);
}
  1. 运行命令自动生成序列化代码:
flutter pub run build_runner build

自动生成的代码会自动处理嵌套字段的null情况,当字段为null时不会调用对应类的fromJson,直接赋值为null,彻底避免类型转换异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 23:54:58