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, }; }
问题原因分析
- 解析逻辑不匹配:API返回的是单个JSON对象,但Flutter代码中把
json.decode(resp.body)得到的Map当成了“键为ID、值为Product对象”的集合,调用forEach遍历时,value是单个字段的值(比如字符串"4"),而非完整的Product对象Map,传入Product.fromMap(value)自然触发类型错误。 - 模型字段不匹配:
- 大小写不一致:API返回的字段是
Monto、Ruta、Pagado,但模型中用json["monto"]、json["ruta"]、json["pagado"](小写开头),导致字段值无法正确映射。 - 类型转换错误:API返回的
Monto是字符串"3222.00",但模型中monto是int类型,直接赋值会触发类型转换错误。
- 大小写不一致:API返回的字段是
- 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
相关产品推荐
相关产品推荐

