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

Flutter中无法将JSON响应映射为类对象的问题求助

解决Flutter JSON解析类型不匹配问题:期望Map<String, dynamic>却得到String列表

问题背景

我遇到了Flutter JSON解析的类型不匹配错误:Error: Expected a value of type 'Map<String, dynamic>', but got one of type 'String'。查过相关资料、跟着官方Cookbook示例尝试后依然没解决。

我想调用一个返回嵌套列表结构的API,原本每个内层字符串列表对应我定义的Provider类对象。我写了两个工厂方法做JSON转对象:

  • factory Provider.fromJsonMap(Map<String, dynamic> json):优先想用的方式,通过字段名访问数据,比索引更灵活
  • factory Provider.fromJsonListDynamic(List<dynamic> json):这个方法能正常工作,但依赖数组索引,扩展性差

API更新后的新问题

之前有人指出我Python API返回的JSON没有结构,没法用json["name"]这种方式访问。于是我修改了Python API,返回序列化后的强类型Provider对象列表,Python端返回前的数据如下:

[
    Provider(name='Google Translate API', id='google.translate_api.v3', img_url='ggl.png'),
    Provider(name='Microsoft Translator', id='microsoft.translator.v3', img_url='mcs.png'),
    Provider(name='Amazon Translate API', id='amazon.translate.api.v2', img_url='aws.png'),
]

但Flutter端用jsonDecode解析后,得到的却是字符串列表,每个元素是Provider对象的字符串表示:

final List<dynamic> listProviders = jsonDecode(response.body);

// listProviders实际内容
[
    "Provider(name='Google Translate API', id='google.translate_api.v3', img_url='ggl.png')",
    "Provider(name='Microsoft Translator', id='microsoft.translator.v3', img_url='mcs.png')",
    "Provider(name='Amazon Translate API', id='amazon.translate.api.v2', img_url='aws.png')",
]

尝试的代码与错误

我尝试把字符串列表转成Map列表再映射成Provider对象,代码如下:

final List<dynamic> listProviders = jsonDecode(response.body);
final List<Map<String, dynamic>> listMap = listProviders.cast<Map<String, dynamic>>();
var providerMap = listMap.map<Provider>((json) => Provider.fromJsonMap(json));
List<Provider> providers = providerMap.toList();

运行后直接抛出之前的类型不匹配错误。

我的代码实现

Provider类

class Provider{    
    final String name;    
    final String id;    
    final String imageUrl;

    Provider( {required this.name, required this.id, required this.imageUrl });

    // 调用失败的方法
    factory Provider.fromJsonMap(Map<String, dynamic> json)    
    {        
        return Provider(
            name:     json["name"]     as String,
            id:       json["id"]       as String,
            imageUrl: json["imageUrl"] as String,
        );    
    }

    // 可以正常运行的方法
    factory Provider.fromJsonListDynamic(List<dynamic> json)    
    {        
        return Provider(
            name:     json[0] as String,
            id:       json[1] as String,
            imageUrl: json[2] as String,
        );    
    }
}

页面State中的异步调用代码

class _DetectorPageState extends State<DetectorPage>{    
    ...

    Future<List<Provider>> getMtProviders() async     
    {        
        Uri url = Uri.parse(SOME_API_URL);        
        http.Response response = await http.get(url);

        if (response.statusCode == 200)         
        {            
            // 无法运行的代码
            final List<dynamic> listProviders = jsonDecode(response.body);            
            final List<Map<String, dynamic>> listMap = listProviders.cast<Map<String, dynamic>>();            
            var providerMap = listMap.map<Provider>((json) => Provider.fromJsonMap(json));            
            List<Provider> providers = providerMap.toList();        
            
            // 可以运行但依赖索引的代码
            // List<dynamic> listProviders = jsonDecode(response.body);            
            // List<Provider> providers = listProviders.map((provider) => Provider.fromJsonListDynamic(provider as List<dynamic>)).toList();

            return providers;        
        }         
        else         
        {            
            print('Request failed with status: ${response.statusCode}.');            
            return [];        
        }    
    }
}

问题根源与解决方案

1. 核心问题

你的Python API返回的不是标准JSON格式,而是把Provider对象直接转成了字符串(相当于Python中str(provider)的结果)。标准JSON应该是键值对结构的数组,比如:

[
    {"name":"Google Translate API","id":"google.translate_api.v3","img_url":"ggl.png"},
    {"name":"Microsoft Translator","id":"microsoft.translator.v3","img_url":"mcs.png"},
    {"name":"Amazon Translate API","id":"amazon.translate.api.v2","img_url":"aws.png"}
]

2. 最优修复方案

第一步:修改Python API返回标准JSON

在Python端,不要直接返回Provider对象列表,而是将每个对象转成字典,再用json.dumps序列化。示例代码:

import json

# 假设providers是你的Provider对象列表
providers_dict = [{"name": p.name, "id": p.id, "img_url": p.img_url} for p in providers]
return json.dumps(providers_dict)

如果用Flask/Django这类框架,直接返回字典列表即可,框架会自动帮你序列化为标准JSON。

第二步:调整Flutter解析代码

API返回标准JSON后,Flutter端就可以正常使用fromJsonMap方法了,同时注意字段名对应(JSON里是img_url,你的类属性是imageUrl):

// 先修正Provider.fromJsonMap的字段映射
factory Provider.fromJsonMap(Map<String, dynamic> json)    
{        
    return Provider(
        name:     json["name"]     as String,
        id:       json["id"]       as String,
        imageUrl: json["img_url"] as String, // 这里改成和JSON一致的字段名
    );    
}

// 再修改调用代码
Future<List<Provider>> getMtProviders() async     
{        
    Uri url = Uri.parse(SOME_API_URL);        
    http.Response response = await http.get(url);

    if (response.statusCode == 200)         
    {            
        final List<dynamic> listProviders = jsonDecode(response.body);            
        List<Provider> providers = listProviders
            .map((json) => Provider.fromJsonMap(json as Map<String, dynamic>))
            .toList();        
            
        return providers;        
    }         
    else         
    {            
        print('Request failed with status: ${response.statusCode}.');            
        return [];        
    }    
}

3. 临时替代方案(无法修改API时)

如果暂时没法修改Python API,可以通过正则表达式从字符串中提取字段值:

// 在Provider类中添加新的工厂方法
factory Provider.fromString(String str) {
    final nameMatch = RegExp(r"name='([^']+)'").firstMatch(str);
    final idMatch = RegExp(r"id='([^']+)'").firstMatch(str);
    final imgUrlMatch = RegExp(r"img_url='([^']+)'").firstMatch(str);
    
    return Provider(
        name: nameMatch?.group(1) ?? "",
        id: idMatch?.group(1) ?? "",
        imageUrl: imgUrlMatch?.group(1) ?? "",
    );
}

// 调用时使用这个方法
List<Provider> providers = listProviders
    .map((str) => Provider.fromString(str as String))
    .toList();

这种方法很脆弱,只要Python端的字符串格式变化就会失效,优先推荐修改API返回标准JSON。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:27:45