【问题标题】:How to deserialize Firestore document with its id using json_serializable in Flutter?如何在 Flutter 中使用 json_serializable 反序列化带有 id 的 Firestore 文档?
【发布时间】:2019-08-15 18:15:08
【问题描述】:

我的 Firestore 数据库中有一个简单的消息文档,其中包含一些字段。

我使用json_serializable 将其反序列化为对象。我的班级如下所示:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';

part 'message_firestore.g.dart';

@JsonSerializable(nullable: true, explicitToJson: true)
class MessageFirestore extends Equatable {
  MessageFirestore(
      this.id, this.content, this.conversationId, this.senderId, this.dateSent);

  factory MessageFirestore.fromJson(Map<String, dynamic> json) =>
      _$MessageFirestoreFromJson(json);

  Map<String, dynamic> toJson() => _$MessageFirestoreToJson(this);

  @JsonKey(name: 'Id')
  final String id;
  @JsonKey(name: 'Content')
  final String content;
  @JsonKey(name: 'ConversationId')
  final String conversationId;
  @JsonKey(name: 'SenderId')
  final String senderId;
  @JsonKey(name: 'DateSent', fromJson: _fromJson, toJson: _toJson)
  final DateTime dateSent;

  static DateTime _fromJson(Timestamp val) =>
      DateTime.fromMillisecondsSinceEpoch(val.millisecondsSinceEpoch);
  static Timestamp _toJson(DateTime time) =>
      Timestamp.fromMillisecondsSinceEpoch(time.millisecondsSinceEpoch);
}

文档中没有名为Id的字段,所以目前它的id没有被反序列化。 但是,从 Firestore 检索到的地图的key 是它的 id,因此可以通过手动反序列化地图来读取此值。 我希望在反序列化期间能够访问文档的 id (_b03002...)。

有什么方法可以配置json_serializable 读取这个id 并将其存储在id 属性中?

【问题讨论】:

  • > 但是,从 Firestore 检索到的地图的关键是它的 id....key 你在说什么?一个 Map 有很多键。
  • 我所说的 key of the map 是屏幕截图上的b0300... id。我发现它在DocumentSnapshot 中作为属性documentID 可用。我希望在反序列化对象时可以访问此 ID。我会尝试在我的问题中澄清这一点。

标签: flutter dart google-cloud-firestore


【解决方案1】:

您可以修改 fromJson 构造函数,以便在第一个参数上提供 id。

factory MessageFirestore.fromJson(String id, Map<String, dynamic> json) {
  return _$MessageFirestoreFromJson(json)..id = id;
}

然后,从你的来电者那里,它会是这样的

Message(snapshot.documentID, snapshot.data)

【讨论】:

  • 嗨,那是什么……叫什么?
  • 谢谢姚斌
  • @Yaobin那么如果类是Message的属性,是否可以在(json_serializable的上下文)中做到这一点?
  • 不确定我是否理解正确,类是消息的属性?也许您想在 Message 类中封装documentID 的传递? factory Message.fromDocumentSnapshot(DocumentSnapshot snapshot) =&gt; _$MessageFromJson(snapshot.data)..id = snapshot.documentID;
【解决方案2】:

您可以在 MessageFirestore 类中添加另一个工厂。

  factory MessageFirestore.fromFire(DocumentSnapshot doc) =>
      _$MessageFirestoreFromFire(doc);

之后,您的课程中将拥有两个工厂函数。

factory MessageFirestore.fromFire(DocumentSnapshot doc) //...

factory MessageFirestore.fromJson(Map<String, dynamic> json) //...

并添加_$MessageFirestoreFromFire(doc) 函数并将_$MessageFirestoreFromJson(json) 函数复制到message_firestore.g.dart 文件中并像这样编辑它:

MessageFirestore _$MessageFirestoreFromFire(DocumentSnapshot doc) {
  return MessageFirestore(
    id: doc.documentID,
    content: doc.data['name'] as String,
    // ... other parameters 
  );
}

在您阅读文件的过程中,您可以:

  Stream<List<MessageFirestore>> getMessageList() {
    return Firestore.instance
        .collection('YourCollectionPath')
        .snapshots()
        .map((snapShot) => snapShot.documents
            .map(
              (document) => MessageFirestore.fromFire(document),
            )
            .toList());
  }

简单易学

而且此方法不会干扰使用 MessageFirestore 实例的其他类。

祝您有美好的一天,并希望此方法对您有用。 :)

【讨论】:

    【解决方案3】:

    改进Yaobin Then的帖子:同时删除toJson上的id:

    
    factory MessageFirestore.fromJson(String id, Map<String, dynamic> json) {
      return _$MessageFirestoreFromJson(json)..id = id;
    }
    
    Map<String, dynamic> toJson() {
      var json = _$MessageFirestoreToJson(this);
      json.removeWhere((key, value) => key == 'id');
      return json;
    }
    

    【讨论】:

      【解决方案4】:

      使用Yaobin Then的答案,我们可以像这样改进它:

      factory MessageFirestore.fromJson(String id, Map<String, dynamic> json) {
        return _$MessageFirestoreFromJson(json)..id = id;
      }
      
      factory MessageFirestore.fromFire(QueryDocumentSnapshot snapshot) {
        return MessageFirestore.fromJson(snapshot.id, snapshot.data() as Map<String, dynamic>);
      }
      

      然后,从你的来电者那里,它会是这样的

      return StreamBuilder<QuerySnapshot>(
        stream: ...,
        builder: (context, snp) {
          final products = snp.data?.docs;
      
          if (products?.isNotEmpty != true) {
            return const Center(
              child: Text('No products'),
            );
          }
      
          final prods = products!.map(
            (prod) {
              return MessageFirestore.fromFire(prod);
            },
          ).toList();
      

      这样您不必在每次调用中填写 2 个参数,fromFire 工厂将为您处理它

      【讨论】:

        猜你喜欢
        • 2019-09-18
        • 2018-12-02
        • 2022-01-05
        • 2019-02-26
        • 2021-06-20
        • 2020-12-05
        • 2019-05-05
        • 2022-07-22
        • 2021-07-20
        相关资源
        最近更新 更多