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

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>

报错原因

  1. 顶层结构处理错误:jsonDecode返回的是包含stories数组的顶层Map,不是数组,直接对其调用cast<Map<String, dynamic>>()完全不符合数据结构;且Map的cast方法需要两个泛型参数(键类型、值类型),不能传入单个Map类型。
  2. 字段解析错误: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 10:07:58