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

Dart/Flutter中API响应解析与类型转换报错及优化疑问

问题背景

我正在调用一个可修改的Node API接口,预期返回特定复杂类型的数据。由于无法在Node后端与Flutter前端之间共享类型,自行定义了如下类型:

enum QuestionType {
  text,
  recording,
  goal,
}

class Question {
  final String question;
  final String? subtitle;
  // final QuestionType type;
  final int? timeInSeconds;
  final int? askGoalAfterDays;
  final bool userSubmitted;

  Question(
      {required this.question,
      // required this.type,
      this.subtitle,
      this.timeInSeconds,
      this.askGoalAfterDays,
      this.userSubmitted = false});
}

调用接口后得到的响应体为:

[{
  "id": 93,
  "createdAt": "2023-03-04T02:35:24.514Z",
  "updatedAt": "2023-03-04T02:36:36.000Z",
  "question": "...",
  "subtitle": null,
  "type": "Gratitude",
  "timeInSeconds": 20,
  "askGoalAfterDays": null,
  "userSubmitted": false
}, {
  "id": 3,
  "createdAt": "2023-03-04T02:35:24.546Z",
  "updatedAt": "2023-03-04T02:35:24.546Z",
  "question": "...",
  "subtitle": null,
  "type": "Rating",
  "timeInSeconds": null,
  "askGoalAfterDays": null,
  "userSubmitted": false
}, {
  "id": 2,
  "createdAt": "2023-03-04T02:35:24.548Z",
  "updatedAt": "2023-03-04T02:36:36.000Z",
  "question": "...",
  "subtitle": null,
  "type": "Goal",
  "timeInSeconds": 20,
  "askGoalAfterDays": 0,
  "userSubmitted": false
}]

调用API并尝试解析内容的代码如下:

static Future<List<QuestionType>> getDaytime() async {
  // ...

  var response = await http.get(url);

  var res = jsonDecode(response.body) as List<QuestionType>; // <-- ERROR

  if (response.statusCode == 200) {
    return [res[0], res[1]];
  } else {
    throw AdException(response.statusCode.toString());
  }
}

解析时出现错误:

Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<QuestionType>' in type cast

疑问:

  1. 为什么会出现这个错误?
  2. 这是解析API返回内容并进行类型转换的最佳方式吗?前后端类型存在明显重复,似乎不够高效。

解答

1. 错误原因

  • jsonDecode返回的是dynamic类型,Dart无法直接将JSON中的字符串(比如"Gratitude"、"Goal")自动映射到你定义的QuestionType枚举值,直接强转必然失败。
  • 核心逻辑错误:API返回的是完整的Question对象列表,但你试图把它转成List<QuestionType>(枚举值列表),两者类型完全不匹配,这是错误的主要诱因。

2. 解析优化与类型重复问题解决

第一步:修正解析逻辑

首先要将JSON数组转换为List<Question>而非List<QuestionType>,需要给Question类添加JSON转对象的构造方法,同时处理枚举与字符串的映射:

补全Question类并添加fromJson方法:

enum QuestionType {
  text,
  recording,
  goal,
  gratitude, // 对应API返回的"Gratitude"
  rating, // 对应API返回的"Rating"
}

class Question {
  final int? id;
  final String? createdAt;
  final String? updatedAt;
  final String question;
  final String? subtitle;
  final QuestionType type;
  final int? timeInSeconds;
  final int? askGoalAfterDays;
  final bool userSubmitted;

  Question({
    this.id,
    this.createdAt,
    this.updatedAt,
    required this.question,
    required this.type,
    this.subtitle,
    this.timeInSeconds,
    this.askGoalAfterDays,
    this.userSubmitted = false,
  });

  // 从JSON构造Question实例
  factory Question.fromJson(Map<String, dynamic> json) {
    // 处理type字段的枚举映射
    final typeStr = json['type'].toLowerCase();
    late QuestionType type;
    switch (typeStr) {
      case 'gratitude':
        type = QuestionType.gratitude;
        break;
      case 'rating':
        type = QuestionType.rating;
        break;
      case 'goal':
        type = QuestionType.goal;
        break;
      case 'text':
        type = QuestionType.text;
        break;
      case 'recording':
        type = QuestionType.recording;
        break;
      default:
        throw ArgumentError('未知的问题类型:$typeStr');
    }

    return Question(
      id: json['id'],
      createdAt: json['createdAt'],
      updatedAt: json['updatedAt'],
      question: json['question'],
      subtitle: json['subtitle'],
      type: type,
      timeInSeconds: json['timeInSeconds'],
      askGoalAfterDays: json['askGoalAfterDays'],
      userSubmitted: json['userSubmitted'],
    );
  }
}

修正API调用的解析代码:

static Future<List<Question>> getDaytime() async {
  // ...

  var response = await http.get(url);

  if (response.statusCode == 200) {
    // 先转成动态列表,再逐个映射为Question对象
    final List<dynamic> jsonList = jsonDecode(response.body);
    return jsonList.map((json) => Question.fromJson(json)).toList();
  } else {
    throw AdException(response.statusCode.toString());
  }
}

第二步:解决前后端类型重复问题

可以通过以下方案减少手动重复定义类型的工作量:

  • 代码生成工具:使用json_serializable配合build_runner,自动生成序列化/反序列化代码,无需手动编写fromJson方法。
  • 后端类型转前端:如果后端用TypeScript开发,可使用typescript-to-dart这类工具,将TS类型自动转换为Dart类和枚举。
  • OpenAPI驱动:让后端提供OpenAPI文档,使用openapi_generator工具自动生成Dart的API调用代码和对应模型类,完全消除手动定义类型的重复工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:25:49