Flutter解析JSON为对象报错NoSuchMethodError问题排查与修复
问题:Storyblok JSON转Flutter Exercise对象触发NoSuchMethodError
尝试将Storyblok CMS返回的JSON映射为Flutter的Exercise对象时,在final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();行报错,错误信息:
NoSuchMethodError: Class '_Map<String, dynamic>' has no instance method 'cast' with matching arguments. Receiver: _Map len:4 Tried calling: cast<Map<String, dynamic>>() Found: cast<Y0, Y1>() => Map<Y0, Y1>
报错原因
- 顶层结构处理错误:jsonDecode返回的是包含
stories数组的顶层Map,不是数组,直接对其调用cast<Map<String, dynamic>>()完全不符合数据结构;且Map的cast方法需要两个泛型参数(键类型、值类型),不能传入单个Map类型。 - 字段解析错误:Exercise类中
image和video定义为String?,但JSON里这两个字段是对象,直接强转成String?会导致类型转换失败。
修复方案
1. 修正JSON解析逻辑
先从顶层Map中取出stories数组,再对数组进行类型转换和映射。
2. 修正image/video字段解析
从对应的对象中提取filename作为字符串值,处理null情况。
3. 优化类型安全
使用?和??避免空指针,减少强制类型转换的风险。
完整修正代码
import 'dart:async'; import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class Exercise { final String title; final String body; final String basecamplevel; final String element; final String? image; final String? video; Exercise({ required this.title, required this.body, required this.basecamplevel, required this.element, this.image, this.video, }); factory Exercise.fromMap(Map<String, dynamic> story) { final content = story['content'] as Map<String, dynamic>; // 解析image对象的filename字段 final imageData = content['image'] as Map<String, dynamic>?; final imageUrl = imageData?['filename'] as String?; // 解析video对象的filename字段 final videoData = content['video'] as Map<String, dynamic>?; final videoUrl = videoData?['filename'] as String?; return Exercise( title: content['title'] as String, body: content['body'] as String, basecamplevel: content['basecamplevel'] as String, element: content['element'] as String, image: imageUrl, video: videoUrl, ); } } Future<List<Exercise>> fetchExercises(http.Client client) async { final response = await client.get(Uri.parse( 'https://api.storyblok.com/v2/cdn/stories?content_type=exercise&token=ACCESS_TOKEN')); return parseExercises(response.body); } List<Exercise> parseExercises(String responseBody) { try { // 先解析顶层Map,取出stories数组 final Map<String, dynamic> responseMap = jsonDecode(responseBody); final List<dynamic> stories = responseMap['stories'] as List; // 将数组中的每个元素转成Map,再映射为Exercise return stories.map((story) => Exercise.fromMap(story as Map<String, dynamic>)).toList(); } catch (e) { debugPrint(e.toString()); return []; } }
关键修改点
- 顶层解析:先获取
responseMap['stories']数组,再逐个处理数组元素 - image/video解析:从对应对象中提取
filename字段,兼容null场景 - 移除
late关键字,构造函数直接初始化可选参数,提升类型安全性
内容的提问来源于stack exchange,提问作者aag
相关产品推荐
相关产品推荐

