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

标签 json firebase flutter dart firebase-realtime-database

我的 Firebase Realtime 数据库中有一个数据库,其中的子项如下所示: enter image description here

这是我在我的 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
  }
}

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

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)
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

当我运行该应用程序时,我收到一条错误消息,指出内部链接的 HashMap 不是映射的子类型,并且在调用 GameAnalytics.fromJson() 时发生错误。我看过多个类似的帖子,但他们的错误提到了 _InternalLinkedHashMap map 。为什么这个链接的 HashMap 在我的响应中使用 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>'

这是我的 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;
  }
}

GameAnalytics.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;
  }
}

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;
  }
}

Timings.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;
  }
}

用户评论.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;
  }
}

游戏数据.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;
  }
}

团队.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;
  }
}

播放器.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;
  }
}

最佳答案

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

jsonDecode(jsonEncode(event.snapshot.value)));

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

关于json - 将 Firebase 实时数据库 json 响应从 _InternalLinkedHashMap<Object?, Object?> 转换为 Map<String,dynamic>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69345005/

相关文章:

java - 身份验证错误 : Unable to respond to any of these challenges: {} Android - 401 Unauthorized

rest - 使用 firebase 的 Spring boot 和 vueJs 身份验证

dart - 如何在flutter中为PhysicalModel添加涟漪效果

ios - 波动的iOS应用程序大小增加,而 Assets /库没有更改

jquery - JSON 对象到字符串

javascript - 我的回调函数似乎没有填充我的数组?

java - 是否可以反/序列化 map 自身在 jackson 中的多态性?

android - Firestore 并发连接是否限制为 1M?

javascript - Firebase firestore 云函数显示错误 : Invalid use of type "undefined" as a Firestore argument

flutter - 在 FAB 上添加表格小部件单击 FLUTTER