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

Flutter使用C# API返回的JSON填充Map时出现类型错误

Flutter解析C# REST API JSON时的类型错误排查与解决

问题描述

在Flutter中尝试用C# REST API返回的JSON填充Map时,出现类型错误:

_TypeError (type 'String' is not a subtype of type 'Map<String, dynamic>')

相关代码与数据

1. C# REST API代码

public async Task Leer([FromForm] string token)  
{
    try
    {
        string jsonResultado = await Clases.AccesoDatos.JsonData("SELECT * FROM vistatransacciones");              
        jsonResultado= jsonResultado.Substring(1);
        int longitud = jsonResultado.Length - 1;
        jsonResultado = jsonResultado.Substring(0,longitud);                
        string simple = "'";
        string dobleComilla = "\x022";
        string json = jsonResultado.Replace(simple,dobleComilla);                
        return Content(json,"application/json" );
    }
    catch (Exception ex)
    {
        return BadRequest(ex.Message);
    }
}

2. API返回的JSON

{"idRegistro":"4","idUsuario":"1","FechaRegistro":"2023-01-25T00:00:00","FechaGasto":"2023-01-25T00:00:00","TipoGasto":"OTRO","TipoTransaccion":"otro","Descripcion":"test","Monto":"3222.00","Nombre":"XXXXX","Ruta":"C:\\DocumentosGastoApp\\coin_dollar_money.ico","Pagado":false,"nota":"Nada"}

3. 目标Map格式

{idRegistro: 4, idUsuario: 1, FechaRegistro: 2023-01-25T00:00:00, FechaGasto: 2023-01-25T00:00:00, TipoGasto: OTRO, TipoTransaccion: otro, Descripcion: test, Monto: 3222.00, Nombre: XXXX, Ruta: C:\\DocumentosGastoApp\\coin_dollar_money.ico, Pagado: false, nota: Nada}

4. Flutter解析JSON的代码

final Map<String, dynamic> productsMap = json.decode(resp.body);
productsMap.forEach((key, value) {
  final tempProduct = Product.fromMap(value);
  tempProduct.id = key;
  products.add(tempProduct);
});

5. Flutter的Product模型代码

import 'dart:convert';

List<Product> productFromMap(String str) =>
    List<Product>.from(json.decode(str).map((x) => Product.fromMap(x)));

String productToMap(List<Product> data) =>
    json.encode(List<dynamic>.from(data.map((x) => x.toMap())));

class Product {
  Product({
    this.idRegistro,
    this.idUsuario,
    this.fechaRegistro,
    this.fechaGasto,
    this.tipoGasto,
    this.tipoTransaccion,
    this.descripcion,
    required this.monto,
    this.nombre,
    this.ruta,
    required this.pagado,
    required this.nota,
    this.id,
  });

  bool pagado;
  String? idRegistro;
  String? idUsuario;
  String? descripcion;
  String? nombre;
  String? ruta;
  DateTime? fechaRegistro;
  DateTime? fechaGasto;
  String? tipoGasto;
  int monto;
  String? tipoTransaccion;
  String nota;
  String? id; //Esto es opcional pues el id no existe al momento de la creacion

  factory Product.fromJson(String str) => Product.fromMap(json.decode(str));

  String toJson() => json.encode(toMap());

  factory Product.fromMap(Map<String, dynamic> json) => Product(
        idRegistro: json["idRegistro"],
        idUsuario: json["idUsuario"],
        fechaRegistro: DateTime.parse(json["FechaRegistro"]),
        fechaGasto: DateTime.parse(json["FechaGasto"]),
        tipoGasto: json["TipoGasto"],
        tipoTransaccion: json["TipoTransaccion"],
        descripcion: json["Descripcion"],
        monto: json["monto"],
        nombre: json["Nombre"],
        ruta: json["ruta"],
        pagado: json["pagado"],
        nota: json["nota"],
        id: json["idRegistro"],
        //picture: json["picture"],
      );

  Map<String, dynamic> toMap() => {
        "idRegistro": idRegistro,
        "idUsuario": idUsuario,
        "FechaRegistro": fechaRegistro,
        "FechaGasto": fechaGasto,
        "TipoGasto": tipoGasto,
        "TipoTransaccion": tipoTransaccion,
        "Descripcion": descripcion,
        "Monto": monto,
        "Nombre": nombre,
        "ruta": ruta,
        "Pagado": pagado,
        "nota": nota,
      };
}

问题原因分析

  1. 解析逻辑不匹配:API返回的是单个JSON对象,但Flutter代码中把json.decode(resp.body)得到的Map当成了“键为ID、值为Product对象”的集合,调用forEach遍历时,value是单个字段的值(比如字符串"4"),而非完整的Product对象Map,传入Product.fromMap(value)自然触发类型错误。
  2. 模型字段不匹配:
    • 大小写不一致:API返回的字段是Monto、Ruta、Pagado,但模型中用json["monto"]、json["ruta"]、json["pagado"](小写开头),导致字段值无法正确映射。
    • 类型转换错误:API返回的Monto是字符串"3222.00",但模型中monto是int类型,直接赋值会触发类型转换错误。
  3. C# API处理风险:手动截取字符串替换引号的方式容易破坏JSON结构,建议使用正规的JSON序列化方式。

解决方案

方案1:修正Flutter解析逻辑(适配单个对象返回)

如果API确实只返回单个对象,直接解析为单个Product实例:

// 替换原解析代码
final Map<String, dynamic> productData = json.decode(resp.body);
final tempProduct = Product.fromMap(productData);
tempProduct.id = productData["idRegistro"]; // 用idRegistro作为id
products.add(tempProduct);

方案2:修改C# API返回数组(推荐,支持多数据场景)

如果业务上可能返回多条数据,修改C# API返回JSON数组,复用现有的productFromMap方法:

public async Task Leer([FromForm] string token)  
{
    try
    {
        // 假设JsonData返回的是包含数组的JSON,比如"[{...}, {...}]"
        string jsonResultado = await Clases.AccesoDatos.JsonData("SELECT * FROM vistatransacciones");              
        // 直接返回,无需手动截取替换(如果JsonData返回格式正确)
        return Content(jsonResultado, "application/json");
    }
    catch (Exception ex)
    {
        return BadRequest(ex.Message);
    }
}

对应的Flutter解析代码改为:

final List<dynamic> productsList = json.decode(resp.body);
products = productsList.map((item) {
  final product = Product.fromMap(item);
  product.id = item["idRegistro"];
  return product;
}).toList();

方案3:修正Product模型的字段映射与类型转换

修改fromMap方法,匹配API字段的大小写,并处理类型转换:

factory Product.fromMap(Map<String, dynamic> json) => Product(
      idRegistro: json["idRegistro"],
      idUsuario: json["idUsuario"],
      fechaRegistro: DateTime.parse(json["FechaRegistro"]),
      fechaGasto: DateTime.parse(json["FechaGasto"]),
      tipoGasto: json["TipoGasto"],
      tipoTransaccion: json["TipoTransaccion"],
      descripcion: json["Descripcion"],
      // 将字符串"3222.00"转换为int,先转double再取整
      monto: double.parse(json["Monto"]).toInt(),
      nombre: json["Nombre"],
      ruta: json["Ruta"], // 匹配API的Ruta字段
      pagado: json["Pagado"], // 匹配API的Pagado字段
      nota: json["nota"],
      id: json["idRegistro"],
    );

额外优化:C# API使用正规JSON序列化

避免手动修改JSON字符串,使用System.Text.Json或Newtonsoft.Json序列化对象:

// 假设从数据库获取的是实体列表
var transacciones = await Clases.AccesoDatos.GetTransacciones("SELECT * FROM vistatransacciones");
string json = JsonSerializer.Serialize(transacciones);
return Content(json, "application/json");

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 15:20:23