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

Flutter Retrofit单API返回两种不同JSON响应的类型转换问题

解决Retrofit多结构JSON响应解析问题

针对同一个API返回两种不同JSON结构,且解析时出现类型转换错误的问题,给你两种实用的解决思路:

方案一:用密封类+统一实体类(推荐)

这种方式类型安全,能避免后续维护时的类型错误。核心思路是利用两个响应的共同字段(count、type、data)定义统一响应类,再用密封类区分不同的data结构:

首先定义密封类区分不同的data类型:

sealed class ApiData {}

// 对应响应1的认证数据
class AuthData extends ApiData {
  final String tokenType;
  final int expiresIn;
  final String accessToken;
  final String refreshToken;
  final int userId;

  AuthData({
    required this.tokenType,
    required this.expiresIn,
    required this.accessToken,
    required this.refreshToken,
    required this.userId,
  });

  factory AuthData.fromJson(Map<String, dynamic> json) {
    return AuthData(
      tokenType: json['token_type'],
      expiresIn: json['expires_in'],
      accessToken: json['access_token'],
      refreshToken: json['refresh_token'],
      userId: json['userId'],
    );
  }
}

// 对应响应2的OTP数据
class OtpData extends ApiData {
  final bool success;
  final bool otpEnabled;
  final String message;
  final String timestamp;
  final int otp;

  OtpData({
    required this.success,
    required this.otpEnabled,
    required this.message,
    required this.timestamp,
    required this.otp,
  });

  factory OtpData.fromJson(Map<String, dynamic> json) {
    return OtpData(
      success: json['success'],
      otpEnabled: json['otpEnabled'],
      message: json['message'],
      timestamp: json['timestamp'],
      otp: json['otp'],
    );
  }
}

然后定义统一的响应实体,根据type字段判断解析哪种data:

class ApiResponse {
  final int count;
  final String type;
  final ApiData data;

  ApiResponse({
    required this.count,
    required this.type,
    required this.data,
  });

  factory ApiResponse.fromJson(Map<String, dynamic> json) {
    final String responseType = json['type'];
    final dataJson = json['data'];
    late ApiData data;

    if (responseType == 'object') {
      data = AuthData.fromJson(dataJson);
    } else if (responseType == 'success') {
      data = OtpData.fromJson(dataJson);
    } else {
      throw Exception('未知响应类型: $responseType');
    }

    return ApiResponse(
      count: json['count'],
      type: responseType,
      data: data,
    );
  }
}

之后在Retrofit接口中直接返回ApiResponse即可:

@GET("your-api-path")
Future<ApiResponse> fetchResponse();

方案二:动态解析+类型判断(快速临时方案)

如果不想定义实体类,可以直接用Map<String, dynamic>作为响应类型,然后在处理响应时根据type字段分支处理,注意手动判断字段类型:

// Retrofit接口定义
@GET("your-api-path")
Future<Map<String, dynamic>> fetchResponse();

// 处理响应的逻辑
void handleApiResponse(Map<String, dynamic> response) {
  final String type = response['type'];
  final data = response['data'];

  if (type == 'object') {
    // 处理认证数据,直接取值并确保类型正确
    String accessToken = data['access_token'] as String;
    int userId = data['userId'] as int;
    // ...后续业务逻辑
  } else if (type == 'success') {
    // 处理OTP数据
    int otp = data['otp'] as int;
    String message = data['message'] as String;
    // ...后续业务逻辑
  }
}

为什么之前的方法会报错?

  • 使用Map<String, String>时,因为响应中存在int类型的字段(比如expires_in、userId、otp),强制转换为String会触发type 'int' is not a subtype of type 'String'错误。
  • 使用Map<String, dynamic>生成文件报错,大概率是Retrofit代码生成器(比如retrofit_generator)对动态类型的支持问题,或者没有正确配置序列化依赖(比如json_serializable)。

内容的提问来源于stack exchange,提问作者Farhana Naaz Ansari

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 16:07:42