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

Flutter中Firestore JSON转Map<String, Model>解码失败问题

问题解决:Firestore中Map类型字段的序列化/反序列化错误

问题背景

我定义了包含Map<String, UserSportAttributes>类型字段的UserData模型类,使用jsonEncode将数据存入Firebase Firestore时,Firebase控制台显示数据正常,但尝试通过jsonDecode将数据转回UserData模型时持续报错,试了多种解码方式都没解决。

相关代码

UserData 类

import 'dart:convert';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:sportmate/Models/user_sport_attributes.dart';

class UserData {
  final String name;
  final int age;
  final bool sex; //0 代表男性,1 代表女性
  final GeoPoint location;
  final String bio;
  final Map<String, UserSportAttributes> sportsPlayed;
  final bool hasSetupAccount;

  UserData({
    required this.name,
    required this.age,
    required this.sex,
    required this.location,
    required this.bio,
    required this.sportsPlayed,
    required this.hasSetupAccount,
  });

  Map<String, dynamic> toJson() => {
        'Name': name,
        'Age': age,
        'Sex': sex,
        'Location': location,
        'Biography': bio,
        'SportsPlayed':
            jsonEncode(sportsPlayed), // 会自动调用toJson吗?
        'HasSetupAccount': hasSetupAccount,
      };

  static fromJson(Map<String, dynamic> json) {
    Map<String, dynamic> sportsPlayedJson = jsonDecode(json['SportsPlayed']);
    return UserData(
        name: json['Name'],
        age: json['Age'],
        sex: json['Sex'],
        location: json['Location'],
        bio: json['Biography'],
        sportsPlayed: Map<String, UserSportAttributes>.from(
            sportsPlayedJson.map((key, value) =>
                MapEntry(key, UserSportAttributes.fromJson(value)))),
        hasSetupAccount: json["HasSetupAccount"]);
  }
}

UserSportAttributes 类

class UserSportAttributes {
  final int level;
  final bool playedProfessional;
  final int yearsOfExperience;

  UserSportAttributes({
    required this.level,
    required this.playedProfessional,
    required this.yearsOfExperience,
  });

  Map<String, dynamic> toJson() => {
        'Level': level,
        'PlayedProfessional': playedProfessional,
        'YearsOfExperience': yearsOfExperience,
      };

  static fromJson(Map<String, dynamic> json) {
    return UserSportAttributes(
        level: json['Level'],
        playedProfessional: json['PlayedProfessional'],
        yearsOfExperience: json['YearsOfExperience']);
  }
}

问题根源及解决方案

你的问题出在toJson方法对sportsPlayed的处理上:Firestore本身支持存储嵌套的Map结构,不需要手动用jsonEncode把它转成字符串。你现在的操作是把Map<String, UserSportAttributes>序列化成JSON字符串存在Firestore里,反序列化时又要把字符串转成Map,这个过程中容易出现类型不匹配或者解析错误。

修改步骤:

  1. 修改UserData的toJson方法:去掉jsonEncode,直接把sportsPlayed转成嵌套Map——因为UserSportAttributes有自己的toJson方法,Dart会自动递归处理:
Map<String, dynamic> toJson() => {
      'Name': name,
      'Age': age,
      'Sex': sex,
      'Location': location,
      'Biography': bio,
      'SportsPlayed': sportsPlayed.map((key, value) => MapEntry(key, value.toJson())),
      'HasSetupAccount': hasSetupAccount,
    };
  1. 修改UserData的fromJson方法:不需要再调用jsonDecode,直接从Firestore返回的Map中解析嵌套结构:
static UserData fromJson(Map<String, dynamic> json) {
  return UserData(
      name: json['Name'],
      age: json['Age'],
      sex: json['Sex'],
      location: json['Location'],
      bio: json['Biography'],
      sportsPlayed: Map<String, UserSportAttributes>.from(
          json['SportsPlayed'].map((key, value) =>
              MapEntry(key, UserSportAttributes.fromJson(value as Map<String, dynamic>)))),
      hasSetupAccount: json["HasSetupAccount"]);
}

(注意:这里给fromJson方法加上了返回类型UserData,让代码更规范;同时给value加上了类型断言as Map<String, dynamic>,避免类型警告)

  1. 清理Firestore中的旧数据:因为之前存的是JSON字符串格式的SportsPlayed,需要删掉旧数据或者重新写入新格式的数据,否则反序列化还是会报错。

这样修改后,Firestore会存储嵌套的Map结构,反序列化时直接解析即可,不需要额外的JSON字符串转义,就能解决报错问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 19:01:20