将 Firebase 实时数据库 json 响应从 _InternalLinkedHashMap<Object?, Object?> 转换为 Map<String,dynamic>

Ste*_*ail 4 json dart firebase firebase-realtime-database flutter

我的 Firebase 实时数据库中有一个子数据库,如下所示: 在此输入图像描述

以下是我在 Flutter 应用程序中通过 firebase 调用收到的 JSON:

{
  "gameAnalytics" : {
    "log" : {
      "20210926073039AbMc4uSXywqpK9OcusSV" : {
        "cityID" : "newYork",
        "countryCode" : "USA",
        "gameID" : "20210927065000Upper90IndnewYofootbiGZYy",
        "gamePaymentMethod" : "payAtPitch",
        "players" : {
          "umZ5ezrtI6a3UoCDWFDc3hInoNA2" : {
            "pnam" : "Mario Rest",
            "url" : "https://i.ibb.co/blahblah.jpg"
          }
        },
        "sportID" : "football",
        "status" : {
          "202109261130395laHd8h77R" : "completing, send back",
          "20210926113039BcUQ8RdbHs" : "payAtPitch",
          "20210926113039Ck9JsD1uf1" : "playersAdded"
        },
        "timeAndDateString" : "20210926073039",
        "totalCost" : 999,
        "type" : "ADD",
        "userWhoAddedID" : "umZ5ezrtI6a3UoCDWFDc3hInoNA2",
        "wantsToBeOrganizer" : true
      },
      "202109261146540focIuCQRi3wNfSluvkl" : {
        "cityID" : "newYork",
        "countryCode" : "USA",
        "gameID" : "20210927065000Upper90IndnewYofootbiGZYy",
        "gamePaymentMethod" : "payByBalance",
        "players" : {
          "hOBQJtqCCNgGBVrAv2MqeaFJmdu1" : {
            "pnam" : "Seong Kang",
            "url" : "messi"
          }
        },
        "promoCodeData" : "U90qaL",
        "sportID" : "football",
        "status" : {
          "2021092615465414NXsxwW51" : "playersAdded",
          "20210926154654A60TLCmS2t" : "paidByBalance",
          "20210926154654VtYR1t4bMZ" : "completing, send back"
        },
        "timeAndDateString" : "20210926114654",
        "totalCost" : 0,
        "type" : "ADD",
        "userWhoAddedID" : "hOBQJtqCCNgGBVrAv2MqeaFJmdu1",
        "wantsToBeOrganizer" : false
      },
      "20210926204533DjF3lMCMDpvwHfsh6lQJ" : {
        "amountToRefund" : 0,
        "promoCodes" : {
          "hOBQJtqCCNgGBVrAv2MqeaFJmdu1" : "U90qaL"
        },
        "status" : {
          "20210927004533ZWGNEMX27V" : "REFUNDING: 0  null  hOBQJtqCCNgGBVrAv2MqeaFJmdu1"
        },
        "type" : "CANCEL",
        "userWhoAddedID" : "hOBQJtqCCNgGBVrAv2MqeaFJmdu1"
      }
    },
    "pitchCost" : 3500,
    "playerNumbers" : {
      "hoursBefore12" : 2,
      "hoursBefore24" : 1,
      "hoursBefore3" : 2,
      "hoursBefore36" : 1,
      "hoursBefore48" : 1,
      "hoursBefore6" : 2,
      "hoursBefore72" : 1,
      "hoursBefore96" : 1
    },
    "timings" : {
      "added" : {
        "20210917004938" : "organiserID12345",
        "20210926113040" : "umZ5ezrtI6a3UoCDWFDc3hInoNA2",
        "20210926154656" : "hOBQJtqCCNgGBVrAv2MqeaFJmdu1"
      },
      "cancelled" : {
        "20210917004939" : "playersUnCancelled",
        "20210927004522" : "playersCancelled"
      },
      "removed" : {
        "20210926113042" : "organiserID12345"
      }
    }
  },
  "gameData" : {
    "addFakePlayers" : false,
    "can" : true,
    "canAddPromoCode" : true,
    "canDes" : {
      "en" : "somedesc"
    },
    "canMes" : "blahblah.",
    "cost" : 999,
    "cur" : 2,
    "currency" : "usd",
    "dat" : "20210927065000",
    "descriptions" : {
      "en" : "blahblah"
    },
    "dur" : "60 minutes",
    "expOrg" : "",
    "gameTypes" : {
      "en" : "blahblah"
    },
    "hostConfirmed" : false,
    "hostInfo" : {
      "hostDescription" : "blahblah",
      "hostNickname" : "Mario R",
      "hostPhoto" : "https://i.ibb.co/blahblah.jpg",
      "isSuperHost" : false
    },
    "lid" : "Upper90IndoorQueensabcde739219515176663407563157034467ap0mr",
    "max" : 15,
    "mes" : "blahblah",
    "mes1" : "my disappointment",
    "mes2" : "is immessurable",
    "mes3" : "and my day",
    "payAtPitchMessage" : {
      "en" : "is ruined"
    },
    "paymentType" : "justOnline",
    "paymentsAllowed" : [ "card" ],
    "pla1" : {
      "hOBQJtqCCNgGBVrAv2MqeaFJmdu1" : {
        "pnam" : "blahblah",
        "url" : "messi"
      }
    },
    "pla2" : {
      "umZ5ezrtI6a3UoCDWFDc3hInoNA2" : {
        "organizer" : true,
        "pnam" : "Mario Rest",
        "url" : "https://i.ibb.co/blahblah.jpg"
      }
    },
    "pub" : false,
    "removalAllowed" : true,
    "showHostConfirmButton" : false,
    "spotEn" : true,
    "surl" : "https://someurl",
    "title" : {
      "en" : "ok"
    }
  },
  "mess" : {
    "-MjlDj5szZxKaDs0p3CN" : {
      "mes" : "ok",
      "tim" : "20210916204938",
      "unm" : "blahblah",
      "usi" : "XwyPhbjzeKNYVczPdCPFTG0DZbj1"
    },
    "-MjlDj6bnMvKUO76EjLE" : {
      "mes" : "ok",
      "tim" : "20210916204938",
      "unm" : "blahblah",
      "usi" : "XwyPhbjzeKNYVczPdCPFTG0DZbj1"
    },
    "-MjlDj7SeqRV0KzRzFFX" : {
      "mes" : "ok",
      "tim" : "20210916204939",
      "unm" : "bllblb",
      "usi" : "XwyPhbjzeKNYVczPdCPFTG0DZbj1"
    },
    "-MjlDj8C-p406IK8I-cb" : {
      "mes" : "msg",
      "tim" : "20210916204940",
      "unm" : "msg",
      "usi" : "XwyPhbjzeKNYVczPdCPFTG0DZbj1"
    }
  },
  "payment" : {
    "playerPaymentStatus" : {
      "hOBQJtqCCNgGBVrAv2MqeaFJmdu1" : {
        "date" : "20210927004533",
        "refundedAmount" : 0,
        "stripePaymentNotConfirmed" : true,
        "type" : "notPaidForGame",
        "userWhoAddedID" : "hOBQJtqCCNgGBVrAv2MqeaFJmdu1"
      }
    }
  },
  "userReview" : {
    "showReview" : true
  }
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用 Map<String,dynamic>.from() 从该响应创建一个Map<String, dynamic>响应,如下所示:

class PickUpGameItem extends StatefulWidget {
  final String gameId;

  const PickUpGameItem(this.gameId, [Key? key]) : super(key: key);

  @override
  _PickUpGameItemState createState() => _PickUpGameItemState();
}

class _PickUpGameItemState extends State<PickUpGameItem> {
  late StreamSubscription _pickUpGameDetailsStreamSub;
  PickUpGameDetails? gameDetails;

  @override
  void initState() {
    super.initState();
    _setListeners();
  }

  @override
  void deactivate() {
    _pickUpGameDetailsStreamSub.cancel();
    super.deactivate();
  }

  void _setListeners() {
    _pickUpGameDetailsStreamSub = FirebaseDatabase()
        .reference()
        .child(
            '.../gamesDetailed/${widget.gameId}/')
        .onValue
        .listen((event) {
      final detailsJson = Map<String, dynamic>.from(event.snapshot.value);
      setState(() {
        gameDetails = PickUpGameDetails.fromJson(detailsJson);
        print(gameDetails.toString());
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
      child: Row(
        children: [
          ClipRRect(
            borderRadius: BorderRadius.circular(8.0),
            child: const Image(
              fit: BoxFit.fill,
              width: 80.0,
              height: 80.0,
              image: AssetImage('assets/images/temp_city_img.jpg'),
            ),
          ),
          const SizedBox(
            width: 10,
          ),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: const [
                Text(
                  'Lorem ipsum dolor sit amet this is a test ......................................',
                  style: TextStyle(
                      color: Colors.black,
                      fontWeight: FontWeight.bold,
                      fontSize: 16),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
                SizedBox(
                  height: 10,
                ),
                Text(
                  'Lorem ipsum dolor sit amet this is a test ......................................',
                  style: TextStyle(
                      color: Colors.grey,
                      fontWeight: FontWeight.normal,
                      fontSize: 14),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              ],
            ),
          ),
          const SizedBox(
            width: 10,
          ),
          Flexible(
            flex: 0,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'Football',
                  style: TextStyle(
                      color: Colors.black,
                      fontWeight: FontWeight.bold,
                      fontSize: 16),
                ),
                const SizedBox(
                  height: 10,
                ),
                Row(
                  children: const [
                    Text(
                      '14/16',
                      style: TextStyle(
                          color: Colors.grey,
                          fontWeight: FontWeight.normal,
                          fontSize: 14),
                    ),
                    SizedBox(
                      width: 5,
                    ),
                    Icon(Icons.ac_unit)
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

当我运行应用程序时,收到一条错误消息,指出内部链接哈希映射不是映射的子类型,并且在调用 GameAnalytics.fromJson() 时发生错误。我看过多个类似的帖子,但他们的错误提到了 _InternalLinkedHashMap<String?,dynamic> 映射。为什么我的响应中的链接哈希映射Object?同时用于键和值?

[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: type '_InternalLinkedHashMap<Object?, Object?>' is not a subtype of type 'Map<String, dynamic>'
Run Code Online (Sandbox Code Playgroud)

这是我的 dart 模型类:

PickUpGameDetails.dart:

class PickUpGameDetails {
  GameAnalytics? gameAnalytics;
  GameData? gameData;
  UserReview? userReview;

  PickUpGameDetails(
      {required this.gameAnalytics,
      required this.gameData,
      required this.userReview});

  PickUpGameDetails.fromJson(Map<String, dynamic> json) {
    gameAnalytics = json['gameAnalytics'] != null
        ? GameAnalytics.fromJson(json['gameAnalytics'])
        : null;
    gameData =
        json['gameData'] != null ? GameData.fromJson(json['gameData']) : null;
    userReview = json['userReview'] != null
        ? UserReview.fromJson(json['userReview'])
        : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    if (gameAnalytics != null) {
      data['gameAnalytics'] = gameAnalytics!.toJson();
    }
    if (gameData != null) {
      data['gameData'] = gameData!.toJson();
    }
    if (userReview != null) {
      data['userReview'] = userReview!.toJson();
    }
    return data;
  }
}
Run Code Online (Sandbox Code Playgroud)

游戏分析.dart:

class GameAnalytics {
  late int pitchCost;
  PlayerNumbers? playerNumbers;
  Timings? timings;

  GameAnalytics(
      {required this.pitchCost,
      required this.playerNumbers,
      required this.timings});

  GameAnalytics.fromJson(Map<String, dynamic> json) {
    pitchCost = json['pitchCost'];
    playerNumbers = json['playerNumbers'] != null
        ? PlayerNumbers.fromJson(json['playerNumbers'])
        : null;
    timings =
        json['timings'] != null ? Timings.fromJson(json['timings']) : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['pitchCost'] = pitchCost;
    if (playerNumbers != null) {
      data['playerNumbers'] = playerNumbers!.toJson();
    }
    if (timings != null) {
      data['timings'] = timings!.toJson();
    }
    return data;
  }
}
Run Code Online (Sandbox Code Playgroud)

PlayerNumbers.dart:

class PlayerNumbers {
  late int hoursBefore12;
  late int hoursBefore24;
  late int hoursBefore3;
  late int hoursBefore36;
  late int hoursBefore48;
  late int hoursBefore6;
  late int hoursBefore72;
  late int hoursBefore96;

  PlayerNumbers(
      {required this.hoursBefore12,
      required this.hoursBefore24,
      required this.hoursBefore3,
      required this.hoursBefore36,
      required this.hoursBefore48,
      required this.hoursBefore6,
      required this.hoursBefore72,
      required this.hoursBefore96});

  PlayerNumbers.fromJson(Map<String, dynamic> json) {
    hoursBefore12 = json['hoursBefore12'];
    hoursBefore24 = json['hoursBefore24'];
    hoursBefore3 = json['hoursBefore3'];
    hoursBefore36 = json['hoursBefore36'];
    hoursBefore48 = json['hoursBefore48'];
    hoursBefore6 = json['hoursBefore6'];
    hoursBefore72 = json['hoursBefore72'];
    hoursBefore96 = json['hoursBefore96'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['hoursBefore12'] = hoursBefore12;
    data['hoursBefore24'] = hoursBefore24;
    data['hoursBefore3'] = hoursBefore3;
    data['hoursBefore36'] = hoursBefore36;
    data['hoursBefore48'] = hoursBefore48;
    data['hoursBefore6'] = hoursBefore6;
    data['hoursBefore72'] = hoursBefore72;
    data['hoursBefore96'] = hoursBefore96;
    return data;
  }
}
Run Code Online (Sandbox Code Playgroud)

计时.dart:

class Timings {
  Map<String, dynamic>? added;
  Map<String, dynamic>? cancelled;
  Map<String, dynamic>? removed;

  Timings(
      {required this.added, required this.cancelled, required this.removed});

  Timings.fromJson(Map<String, dynamic> json) {
    added = json['added'];
    cancelled = json['cancelled'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    if (added != null) {
      data['added'] = added;
    }
    if (cancelled != null) {
      data['cancelled'] = cancelled;
    }
    if (removed != null) {
      data['removed'] = removed;
    }
    return data;
  }
}
Run Code Online (Sandbox Code Playgroud)

用户评论.dart:

class UserReview {
  bool? showReview;

  UserReview({required this.showReview});

  UserReview.fromJson(Map<String, dynamic> json) {
    showReview = json['showReview'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['showReview'] = showReview;
    return data;
  }
}
Run Code Online (Sandbox Code Playgroud)

游戏数据.dart:

class GameData {
  late bool addFakePlayers;
  late bool hasBeenCancelled;
  late bool canAddPromoCode;
  late String cancellationDescription;
  late String cancellationMsg;
  late int cost;
  late int cur;
  late String currency;
  late String dateTime;
  late String description;
  late String durationMsg;
  late String expOrg;
  late String gameTypeMsg;
  late bool hostConfirmed;
  late String lid;
  late int maxPlayers;
  late List<String> messages;
  late String payAtPitchMessage;
  late String paymentType;
  late List<String> paymentsAllowed;
  Team? team1;
  Team? team2;
  late bool pub;
  late bool removalAllowed;
  late bool showHostConfirmButton;
  late bool spotEn;
  late String surl;
  late String title;

  GameData(
      {required this.addFakePlayers,
      required this.hasBeenCancelled,
      required this.canAddPromoCode,
      required this.cancellationDescription,
      required this.cancellationMsg,
      required this.cost,
      required this.cur,
      required this.currency,
      required this.dateTime,
      required this.description,
      required this.durationMsg,
      required this.expOrg,
      required this.gameTypeMsg,
      required this.hostConfirmed,
      required this.lid,
      required this.maxPlayers,
      required this.messages,
      required this.payAtPitchMessage,
      required this.paymentType,
      required this.paymentsAllowed,
      required this.team1,
      required this.team2,
      required this.pub,
      required this.removalAllowed,
      required this.showHostConfirmButton,
      required this.spotEn,
      required this.surl,
      required this.title});

  GameData.fromJson(Map<String, dynamic> json) {
    addFakePlayers = json['addFakePlayers'];
    hasBeenCancelled = json['can'];
    canAddPromoCode = json['canAddPromoCode'];
    cancellationDescription = json['canDes']['en'];
    cancellationMsg = json['canMes'];
    cost = json['cost'];
    cur = json['cur'];
    currency = json['currency'];
    dateTime = json['dat'];
    description = json['descriptions']['en'];
    durationMsg = json['dur'];
    expOrg = json['expOrg'];
    gameTypeMsg = json['gameTypes']['en'];
    hostConfirmed = json['hostConfirmed'];
    lid = json['lid'];
    maxPlayers = json['max'];
    messages.add(json['mes']);
    messages.add(json['mes1']);
    messages.add(json['mes2']);
    messages.add(json['mes3']);
    payAtPitchMessage = json['payAtPitchMessage']['en'];
    paymentType = json['paymentType'];
    paymentsAllowed = json['paymentsAllowed'].cast<String>();
    team1 = json['pla1'] != null ? Team.fromJson(json['pla1']) : null;
    team2 = json['pla2'] != null ? Team.fromJson(json['pla2']) : null;
    pub = json['pub'];
    removalAllowed = json['removalAllowed'];
    showHostConfirmButton = json['showHostConfirmButton'];
    spotEn = json['spotEn'];
    surl = json['surl'];
    title = json['title']['en'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['addFakePlayers'] = addFakePlayers;
    data['can'] = hasBeenCancelled;
    data['canAddPromoCode'] = canAddPromoCode;
    data['canDes'] = cancellationDescription;
    data['canMes'] = cancellationMsg;
    data['cost'] = cost;
    data['cur'] = cur;
    data['currency'] = currency;
    data['dat'] = dateTime;
    data['descriptions'] = description;
    data['dur'] = durationMsg;
    data['expOrg'] = expOrg;
    data['gameTypes'] = gameTypeMsg;
    data['hostConfirmed'] = hostConfirmed;
    data['lid'] = lid;
    data['max'] = maxPlayers;
    data['mes'] = messages.elementAt(0);
    data['mes1'] = messages.elementAt(1);
    data['mes2'] = messages.elementAt(2);
    data['mes3'] = messages.elementAt(3);
    data['payAtPitchMessage'] = payAtPitchMessage;
    data['paymentType'] = paymentType;
    data['paymentsAllowed'] = paymentsAllowed;
    data['pla1'] = team1?.toJson();
    data['pla2'] = team2?.toJson();
    data['pub'] = pub;
    data['removalAllowed'] = removalAllowed;
    data['showHostConfirmButton'] = showHostConfirmButton;
    data['spotEn'] = spotEn;
    data['surl'] = surl;
    data['title'] = title;
    return data;
  }
}
Run Code Online (Sandbox Code Playgroud)

团队.dart:

class Team {
  Map<String, Player> players;

  Team({required this.players});

  factory Team.fromJson(Map<String, dynamic> json) {
    Map<String, Player> _players = {};
    for (String key in json.keys) {
      _players[key] = Player.fromJson(json[key]);
    }
    return Team(players: _players);
  }

  Map<String, dynamic> toJson() {
    Map<String, dynamic> json = <String, dynamic>{};
    for (String key in players.keys) {
      json[key] = players[key];
    }
    return json;
  }
}
Run Code Online (Sandbox Code Playgroud)

玩家.dart:

class Player {
  String name;
  String url;

  Player({required this.name, required this.url});

  factory Player.fromJson(Map<String, dynamic> json) {
    return Player(name: json['pname'], url: json['url']);
  }

  Map<String, dynamic> toJson() {
    final json = <String, dynamic>{};
    json['pname'] = name;
    json['url'] = url;
    return json;
  }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ail 6

在搜索了 Flutter 存储库上的多个 Stack 帖子和 GitHub 问题后,我发现了这条神奇的线,由于某种奇怪的原因,它是目前让事情正常运行的唯一方法。在 flutter 存储库上提出此建议的人也提到了它有多么奇怪,但我猜测 Firebase 的响应不是有效的 JSON 格式(?)。现在,如果你想要一些有用的东西,可能就是这样:

jsonDecode(jsonEncode(event.snapshot.value)));
Run Code Online (Sandbox Code Playgroud)

请注意,这可能对性能不利。