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

Flutter解析JSON报错:String不是Map<String,dynamic>子类型

Flutter请求Flask接口类型转换异常排查

异常场景

Flutter开发过程中,请求本地Flask服务接口 http://192.168.1.24:5000/analyze 获取加密货币数据时,抛出未处理异常:

Unhandled Exception: type 'String' is not a subtype of type 'Map<String, dynamic>'

已知信息

  • 接口返回结构:Flask接口返回JSON数组,数组内为加密货币数据条目,包含atl_change_percentage、image、market_cap_rank等加密货币行情字段
  • 现有实现:
    • 已定义CryptoInfo实体类,实现了接收Map<String, dynamic>入参的fromJson工厂构造方法
    • 网络请求逻辑使用http.get发起,通过jsonDecode解析响应体
    • 页面层使用FutureBuilder组件实现加载态、错误态、数据态的三态渲染
  • 异常表现:
    控制台打印接口响应状态码为200,可正常输出data[0]的完整内容,但执行CryptoInfo.fromJson(data[0])时触发类型不匹配错误,数据无法完成实体转换,页面不能正常渲染数据。

根因分析

接口返回的数组元素实际是JSON格式的字符串,而非结构化的键值对对象。jsonDecode仅处理了最外层的响应体,数组内的字符串元素没有被自动反序列化为Map<String, dynamic>类型,传入要求Map类型入参的fromJson方法时,就会触发类型不匹配错误。

修复方案

转换实体前,先判断数组元素的类型,如果是字符串类型,先做二次jsonDecode拿到Map结构,再传入实体类构造方法,核心实现代码如下:

import 'dart:convert';
import 'package:http/http.dart' as http;

Future<List<CryptoInfo>> fetchCryptoData() async {
  final response = await http.get(Uri.parse('http://192.168.1.24:5000/analyze'));
  if (response.statusCode == 200) {
    // 解析外层响应体
    final List<dynamic> rawData = jsonDecode(response.body);
    return rawData.map((item) {
      // 二次解析数组内的JSON字符串
      final Map<String, dynamic> itemMap = 
          item is String ? jsonDecode(item) : item as Map<String, dynamic>;
      return CryptoInfo.fromJson(itemMap);
    }).toList();
  } else {
    throw Exception('接口请求失败,状态码:${response.statusCode}');
  }
}

校验方式

如果调整后仍存在问题,可在转换前打印data[0].runtimeType确认实际类型:

  • 打印结果为String时,上述二次解析方案可直接解决问题
  • 打印结果为其他类型时,可排查Flask端的序列化逻辑,确认接口返回时没有将数据对象手动转成字符串后再放入数组。

内容的提问来源于stack exchange,提问作者Bill.xby

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:51:07