Reputation: 2543
I have a simple Message document in my Firestore database that has some fields.
I use json_serializable
to deserialize it to object. My class looks like follows:
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);
}
There is no field called Id
in the document, so currently its id is not being deserialized.
However, the
I wish to have access to the id of the document (_b03002...) during deserialization.key
of the map retrieved from Firestore is its id, so this value can be read by manually deserializing the map.
Is there any way to configure json_serializable
to read this id and store it in id
property?
Upvotes: 6
Views: 4702
Reputation: 1281
using Yaobin Then's answer, we can improve it forward like this:
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>);
}
Then, from your caller, it'll be something like this
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();
this way you do not have to fill the 2 arguments in every call, the fromFire
factory will handle it for you
Upvotes: 0
Reputation: 1846
Improvement to Yaobin Then's post: Also remove id on toJson
:
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;
}
Upvotes: 2
Reputation: 163
You could add another factory into MessageFirestore class.
factory MessageFirestore.fromFire(DocumentSnapshot doc) =>
_$MessageFirestoreFromFire(doc);
after that you will have two factory function in you class.
factory MessageFirestore.fromFire(DocumentSnapshot doc) //...
factory MessageFirestore.fromJson(Map<String, dynamic> json) //...
and add _$MessageFirestoreFromFire(doc)
function with copying _$MessageFirestoreFromJson(json)
function into message_firestore.g.dart
file and edit it like this:
MessageFirestore _$MessageFirestoreFromFire(DocumentSnapshot doc) {
return MessageFirestore(
id: doc.documentID,
content: doc.data['name'] as String,
// ... other parameters
);
}
and in you service to reading the documents you can:
Stream<List<MessageFirestore>> getMessageList() {
return Firestore.instance
.collection('YourCollectionPath')
.snapshots()
.map((snapShot) => snapShot.documents
.map(
(document) => MessageFirestore.fromFire(document),
)
.toList());
}
Easy Peasy
And also this method doesn't interference with other classes that use MessageFirestore instance.
Have a nice day and wish this method works for you. :)
Upvotes: 2
Reputation: 2832
You could modify the fromJson
constructor, so that you'd provide the id on the first parameter.
factory MessageFirestore.fromJson(String id, Map<String, dynamic> json) {
return _$MessageFirestoreFromJson(json)..id = id;
}
Then, from your caller, it'll be something like this
Message(snapshot.documentID, snapshot.data)
Upvotes: 14