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
疑问:
- 为什么会出现这个错误?
- 这是解析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
相关产品推荐
相关产品推荐

