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

Flutter解析含Null的登录JSON遇类型异常,求空值处理方案

登录响应JSON解析问题修复

问题概述

在解析登录接口的JSON响应时,遇到两个核心问题:

  1. 登录失败(用户名/密码无效)时,userData字段为null,但模型类未处理空值,引发解析异常
  2. 抛出类型转换异常:type '_Map<String, dynamic>' is not a subtype of type 'ErrorDetails'

响应示例

  • 用户名无效时的响应:
{    
    "userData": null,    
    "verificationStatus": false,    
    "errorDetails": [        
        {            
            "errorMap": {                
                "Username": "The Username Is Invalid"            
            }        
        }    
    ],    
    "siteUserHBList": null
}
  • 密码无效时的响应:
{    
    "userData": null,    
    "verificationStatus": false,    
    "errorDetails": [        
        {            
            "errorMap": {                
                "Password": "The Password Is Invalid"            
            }        
        }    
    ],    
    "siteUserHBList": null
}

当前模型与解析代码

模型类及解析逻辑如下:

import 'dart:convert';

class LoginResponse {
  final UserData userData;
  final bool verificationStatus;
  final List<ErrorDetails> errorDetails;

  LoginResponse({required this.userData, required this.verificationStatus, required this.errorDetails});

  factory LoginResponse.fromJson(Map<String, dynamic> json) {
    return LoginResponse(
        verificationStatus: json['verificationStatus'] as bool,
        userData: UserData.fromJson(json['userData']) ,
        errorDetails: List<ErrorDetails>.from(json["errorDetails"].map((x) => ErrorDetails.fromJson(x))),
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['userData'] = this.userData;
    data['verificationStatus'] = this.verificationStatus;
    data['errorDetails'] = List<dynamic>.from(errorDetails.map((x) => x.toJson()));
    return data;
  }
}

class ErrorDetails
{
  final ErrorMap errorMap;

  ErrorDetails({required this.errorMap });

  factory ErrorDetails.fromJson(Map<String, dynamic> json) {
    return ErrorDetails(
        errorMap: ErrorMap.fromJson(json['errorMap'])
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['errorMap'] = this.errorMap;
    return data;
  }
}

class ErrorMap
{
  final String? Username; // nullable
  final String? Password; // nullable

  ErrorMap({this.Username, this.Password});

  factory ErrorMap.fromJson(Map<String, dynamic> json)
  {
    return ErrorMap
      (
      Username: json['Username'] as String?,
      Password: json['Password'] as String?,
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['Username'] = Username;
    data['Password'] = Password;
    return data;
  }
}

class UserData{
  final int userId;
  final int applicationId;
  final int siteId;
  final String username;
  final String email;
  final String firstName;
  final String lastName;
  final String profileImageUrl;

  UserData({required this.userId, required this.applicationId, required this.siteId, required this.username,
  required this.email, required this.firstName, required this.lastName, required this.profileImageUrl});

  factory UserData.fromJson(Map<String, dynamic> json) {
    return UserData(
      userId: json['userId'] as int,
      applicationId: json['applicationId'] as int,
      siteId: json['siteId'] as int,
      username: json['username'] as String,
      email: json['email'] as String,
      firstName: json['firstName'] as String,
      lastName: json['lastName'] as String,
      profileImageUrl: json['profileImageUrl'] as String,
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['userId'] = this.userId;
    data['applicationId'] = this.applicationId;
    data['siteId'] = this.siteId;
    data['username'] = this.username;
    data['email'] = this.email;
    data['firstName'] = this.firstName;
    data['lastName'] = this.lastName;
    data['profileImageUrl'] = this.profileImageUrl;
    return data;
  }
}

解析代码:

Map<String, dynamic> parsedData = jsonDecode(response.body);
LoginResponse loginResponse = LoginResponse.fromJson(parsedData);
print('errormap:>>$loginResponse.errorDetails[0].errorMap.Username');

抛出的异常

type '_Map<String, dynamic>' is not a subtype of type 'ErrorDetails'


问题原因分析

  1. userData空值未处理:登录失败时userData为null,但LoginResponse中userData被定义为非空类型,且fromJson直接调用UserData.fromJson(json['userData']),此时null无法转为Map<String, dynamic>,触发的异常会干扰后续解析逻辑,导致错误提示指向ErrorDetails。
  2. errorDetails空值与格式兼容问题:若登录成功时后端返回errorDetails为null,当前解析逻辑会因null无法调用map方法报错;若后端偶尔返回单个ErrorDetails对象而非数组,也会触发类型转换异常。

修复方案

1. 处理userData的空值

修改LoginResponse类,将userData改为可空类型,并在解析时添加空值判断:

class LoginResponse {
  final UserData? userData; // 改为可空类型
  final bool verificationStatus;
  final List<ErrorDetails>? errorDetails; // 同时处理errorDetails的空值

  LoginResponse({this.userData, required this.verificationStatus, this.errorDetails});

  factory LoginResponse.fromJson(Map<String, dynamic> json) {
    return LoginResponse(
        verificationStatus: json['verificationStatus'] as bool,
        // 仅当userData不为null时解析
        userData: json['userData'] != null ? UserData.fromJson(json['userData']) : null,
        // 处理errorDetails的空值与格式兼容
        errorDetails: json["errorDetails"] != null 
            ? (json["errorDetails"] is List 
                ? List<ErrorDetails>.from(json["errorDetails"].map((x) => ErrorDetails.fromJson(x)))
                : [ErrorDetails.fromJson(json["errorDetails"])]) // 单个对象转为数组
            : [], // 或根据需求改为null
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['userData'] = this.userData?.toJson(); // 可空类型调用方法需加?
    data['verificationStatus'] = this.verificationStatus;
    data['errorDetails'] = errorDetails?.map((x) => x.toJson()).toList();
    return data;
  }
}

2. 优化错误信息读取逻辑

在main.dart中读取错误信息时,添加空值判断避免索引越界:

Map<String, dynamic> parsedData = jsonDecode(response.body);
LoginResponse loginResponse = LoginResponse.fromJson(parsedData);

if (loginResponse.errorDetails != null && loginResponse.errorDetails!.isNotEmpty) {
  var errorMap = loginResponse.errorDetails![0].errorMap;
  if (errorMap.Username != null) {
    print('用户名错误: ${errorMap.Username}');
  } else if (errorMap.Password != null) {
    print('密码错误: ${errorMap.Password}');
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 20:20:22