Flutter freezed convert List to json - json

this is my two freezed class which i want to make a simple json from ContactsData
#freezed
class ContactsData with _$ContactsData {
const factory ContactsData({
String? displayName,
String? givenName,
String? familyName,
String? company,
String? jobTitle,
List<ContactPhone>? phones,
}) = _ContactsData;
factory ContactsData.fromJson(Map<String, dynamic> json) => _$ContactsDataFromJson(json);
}
#freezed
class ContactPhone with _$ContactPhone {
const factory ContactPhone({
String? label,
String? value,
}) = _ContactPhone;
factory ContactPhone.fromJson(Map<String, dynamic> json) => _$ContactPhoneFromJson(json);
}
i added some data into allContacts by this code:
late List<ContactsData> allContacts=[];
contacts?.forEach((c) {
List<ContactPhone> phones=[];
c.phones!.forEach((f) =>phones.add(ContactPhone(label: f.label,value: f.value)));
allContacts.add(
ContactsData(
displayName:c.displayName,
givenName: c.givenName,
familyName: c.familyName,
company: c.company,
jobTitle: c.jobTitle,
phones: phones,
)
);
});
now how can i convert allContacts to json like with this code:
allContacts.toJson();
build.yaml:
targets:
$default:
builders:
json_serializable:
options:
explicit_to_json: true

allContacts is just a regular List.
Either:
make it an object (possibly even a freezed object) similar to ContactsData and call it something like ContactsCollection or something better... and add toJson method to it.
Or
Do something like jsonEncode(allContacts.map((c) => c.toJson()).toList())

Related

Convert DocumentReference property into String from Firestore

I have a list of elements (Tickets) and since I can't convert the json because of one property (in this case user_id) being a document reference, how can I achieve to change only this property into a string (user_id)? It is written in Dart.
Code that won't work with DocumentReference:
QuerySnapshot<Map<String, dynamic>> querySnapshot.docs
.map((doc) => Ticket.fromJson(doc.data()))
.toList();
fromJson:
Ticket _$TicketFromJson(Map<String, dynamic> json) => Ticket(
id: json['id'] as String? ?? '',
createdAt: DateTime.parse(json['created_at'] as String),
price: (json['price'] as num).toDouble(),
userId: json['user_id'] as String,
);
I know that I can get the String path of the DocumentReference by using .getPath(). But how can alter it inside the map in the query above?
Here is a working solution:
final result = querySnapshot.docs.map((doc) {
final ticket = doc.data();
final dR = ticket['user_id'] as DocumentReference;
final path = dR.path;
ticket.update('user_id', (dynamic value) => path);
return Ticket.fromJson(ticket);
}).toList();
Maybe there is a more elegant way to accomplish the same result.

Dart map - type 'String' is not a subtype of type 'int' of 'index'

I'm learning flutter by making an app following some youtube tutorials.
I have problem with showing search results in the app view. I'm able to query and get data from node backend but there's this error while mapping the json to model.
The data I'm getting from api is like this:
{id: <uuid>,
userEmail: <email_string>,
profile: [{profileName: <profile_name_string>,
profileImage: <image_url_string>,
profileBio: <profile_bio_string>}]
}
The error shows up in the profileName mapping line in the model.dart file.
class AccountModel {
String userId;
String userEmail;
String? userPassword;
AccountModel({
required this.userId,
required this.userEmail,
this.userPassword,
});
}
class ProfileModel {
AccountModel accountModel;
String? profileName;
String profileImage;
String? profileBio;
ProfileModel({
required this.accountModel,
this.profileName,
required this.profileImage,
this.profileBio,
});
factory ProfileModel.fromMap({required Map<String, dynamic> map}) {
print(map);
return ProfileModel(
profileName: map['profile']['profileName'],
profileImage: map['profile']['profileImage'] ?? "default",
profileBio: map['profile']['profileBio'],
accountModel: AccountModel(
userId: map['id'],
userEmail: map['userEmail'],
userPassword: map['userPassword'],
),
);
}
factory ProfileModel.fromMapFollowerData(
{required Map<String, dynamic> map}) {
return ProfileModel(
profileName: map['profileName'],
profileImage: map['profileImage'] ?? "default",
profileBio: map['profileBio'],
accountModel: AccountModel(
userId: map['userId'],
userEmail: map['userEmail'],
),
);
}
}
Maybe I don't understand this correctly but I think that because the profile data is in [] I need to specify index.
How to rectify this error?
EDIT: New model after updating as guided by Majid:
class AccountModel {
String userId;
String userEmail;
String? userPassword;
final List<ProfileModel>? profile;
AccountModel({
required this.userId,
required this.userEmail,
this.userPassword,
this.profile,
});
factory AccountModel.fromJson({required Map<String, dynamic> map}) {
return AccountModel(
userId: map['id'],
userEmail: map['userEmail'],
userPassword: map['userPassword'],
profile: map['profile']
.map((profileJson) => ProfileModel.fromJson(profileJson))
.toList(),
);
}
}
class ProfileModel {
String profileName;
String profileImage;
String? profileBio;
ProfileModel({
required this.profileName,
required this.profileImage,
this.profileBio,
});
factory ProfileModel.fromJson(profileJson, {Map<String, dynamic>? map}) {
if (map != null) {
return ProfileModel(
profileName: map['profileName'],
profileImage: map['profileImage'] ?? "default",
profileBio: map['profileBio'],
);
} else {
return ProfileModel(
profileName: profileJson['profileName'],
profileImage: profileJson['profileImage'] ?? "default",
profileBio: profileJson['profileBio'],
);
}
}
}
I was getting some errors because of how some functions in other pages that use the model are set up, so I made a few changes. I don't have errors in account/profile creation, post creation pages but still with the search page I have error. The error with the list now is:type 'List<dynamic>' is not a subtype of type 'List<ProfileModel>?'.
The error is where account model is mapping profile model and adding it to list. I tried making the list typed like:.toList<ProfileModel>(),, which I'm sure was stupid coz it shows NoSuchMethodError (NoSuchMethodError: Class 'MappedListIterable<dynamic, dynamic>' has no instance method 'toList' with matching arguments. Receiver: Instance of 'MappedListIterable<dynamic, dynamic>' Tried calling: toList<ProfileModel>() Found: toList({bool growable}) => List<X0>)
The problem here is that profile is a List or Array of Map<String, String> here is your example
...
profile: [
{profileName: <profile_name_string>,
profileImage: <image_url_string>,
profileBio: <profile_bio_string>}
]
which means when you want to access this you have to iterate over the List to convert them all.
Which means this line in your model would become
profileName: map['profile'][0]['profileName'],
profileImage: map['profile'][0]['profileImage'] ?? "default",
profileBio: map['profile'][0]['profileBio'],
However, this has a potential problem. Because you might have a List with no members which mean map['profile'][0] might be empty or null and another problem is that how about a time that profile has more than one member like you have map['profile][0] and map['profile'][1] and so on? in this case you are missing some of the members.
You can stick to only 0 index if you are sure that you always have a member and only one in your profile List but if you want to do this better you should probably do this like:
class UserAccountModel {
UserAccountModel({
this.id,
this.userEmail,
this.profile,
});
final String? id;
final String? userEmail;
final List<ProfileModel>? profile;
factory UserAccountModel.fromJson(Map<String, dynamic> map) {
final profile = map['profile'] as List<dynamic> ;
return UserAccountModel(
id: map['id'],
userEmail: map['userEmail'],
profile: profile.map((profileJson) => ProfileModel.fromJson(profileJson)).toList(),
);
}
}
class ProfileModel {
ProfileModel({
this.profileName,
this.profileImage,
this.profileBio,
});
final String? profileName;
final String? profileImage;
final String? profileBio;
factory ProfileModel.fromJson(Map<String, String> map) {
return ProfileModel(
profileName: map['profileName'],
profileImage: map['profileImage'] ?? "default",
profileBio: map['profileBio'],
);
}
}
I strongly suggest you use json_serializable to avoid any mistakes.

How to corect toJson converter in freezed lib for dart/flutter

When trying to convert, the Profile class is not converted correctly. Exited as the result of the toString () function.
Person.dart
import 'package:adminapp/domains/Test/Profile.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'Person.freezed.dart';
part 'Person.g.dart';
#freezed
class Person with _$Person {
factory Person({
String? id,
Profile? profile,
}) = _Person;
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
}
Profile.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'Profile.freezed.dart';
part 'Profile.g.dart';
#freezed
class Profile with _$Profile {
factory Profile({
DateTime? bDay,
String? hob,
String? rel,
}) = _Profile;
factory Profile.fromJson(Map<String, dynamic> json) =>
_$ProfileFromJson(json);
}
main.dart
import 'package:adminapp/domains/Test/Person.dart';
import 'package:adminapp/domains/Test/Profile.dart';
void main(List<String> args) {
Person p = Person(
id: '4',
profile: Profile(
bDay: DateTime.now(),
hob: "123",
rel: 'asd',
));
print(p.toJson());
}
output:
{id: 4, profile: Profile(bDay: 2021-07-28 08:42:51.708857, hob: 123, rel: asd)}
But it's not json format! Profile class convert dont corect!
And I cant save it to firestore!
Your desired valid json string:
{id: 4, profile: {bDay: 2022-08-08T14:54:11.781502, hob: 123, rel: asd}}
From documentation:
In order to serialize nested lists of freezed objects, you are
supposed to either specify a #JsonSerializable(explicitToJson: true)
or change explicit_to_json inside your build.yaml file.
After in generated class will be one small change, pls see the pic below:

How to add an Object with a DocumentReference type in firebase (Flutter/dart)?

I want to reference a category document in my post document in firebase.
This is my data class, I'm also using freezed and json_serializer:
part 'post_dto.freezed.dart';
part 'post_dto.g.dart';
part 'category_dto.freezed.dart';
part 'category_dto.g.dart';
#freezed
abstract class PostDTO with _$PostDTO {
const PostDTO._();
const factory PostDTO({
#JsonKey(ignore: true) String? id,
required String title,
required String description,
#DocumentReferenceConveter() DocumentReference? categoryReference,
}) = _PostDTO;
factory PostDTO.fromJson(Map json) =>
_$PostDTOFromJson(json);
factory PostDTO.fromFireStore(DocumentSnapshot document) {
Map data = document.data() as Map;
return PostDTO.fromJson(data).copyWith(id: document.id);
}
}
#freezed
abstract class CategoryDTO with _$CategoryDTO {
const CategoryDTO._();
const factory CategoryDTO({
required String icon,
required String name,
}) = _CategoryDTO;
factory CategoryDTO.fromFireStore(DocumentSnapshot document) {
Map data = document.data() as Map;
return CategoryDTO.fromJson(data);
}
factory CategoryDTO.fromJson(Map json) =>
_$CategoryDTOFromJson(json);
}
When I run build_runner I got this error:
[SEVERE] json_serializable:json_serializable on lib/infrastructure/post/post_dto.dart:
Could not generate `fromJson` code for `categoryReference`.
To support the type `DocumentReference` you can:
* Use `JsonConverter`
https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html
* Use `JsonKey` fields `fromJson` and `toJson`
https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html
https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html
package:UPLFY/infrastructure/post/post_dto.freezed.dart:373:41
╷
373 │ final DocumentReference? categoryReference;
│ ^^^^^^^^^^^^^^^^^
╵
[INFO] Running build completed, took 2.5s
[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 44ms
[SEVERE] Failed after 2.5s
So tried using the JsonConverter but I'm not sure how to convert the json object to a DocumentReference...
class DocumentReferenceConveter
implements JsonConverter, Object> {
const DocumentReferenceConveter();
#override
DocumentReference fromJson(Object json) {
return //TODO: Convert json to DocumentReference
}
#override
Object toJson(DocumentReference documentReference) =>
documentReference;
}
I was able to put together my solution from the research I found online and so far came up with this.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:json_annotation/json_annotation.dart';
class DocumentReferenceJsonConverter
implements JsonConverter<DocumentReference?, Object?> {
const DocumentReferenceJsonConverter();
#override
DocumentReference? fromJson(Object? json) {
return tryCast<DocumentReference>(json);
}
#override
Object? toJson(DocumentReference? documentReference) => documentReference;
}
T? tryCast<T>(value) {
return value == null ? null : value as T;
}
...
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_profile.freezed.dart';
part 'user_profile.g.dart';
#freezed
class UserProfile with _$UserProfile {
const UserProfile._();
#TimestampConverter()
#DocumentReferenceJsonConverter()
#JsonSerializable(
explicitToJson: true,
fieldRename: FieldRename.snake,
includeIfNull: false,
)
factory UserProfile({
#JsonKey(ignore: true) DocumentReference? reference,
String? avatarUrl,
required String email,
required String firstName,
required String lastName,
Gender? gender,
DateTime? birthday,
String? additionalInfo,
Contact? contact,
DocumentReference? familyReference,
DateTime? createdAt,
}) = _UserProfile;
factory UserProfile.empty() => UserProfile(email: '', firstName: '', lastName: '');
factory UserProfile.fromJson(Map<String, dynamic> json) => _$UserProfileFromJson(json);
factory UserProfile.fromDocument(DocumentSnapshot documentSnapshot) {
final data = documentSnapshot.data();
return data != null
? UserProfile.fromJson(data as Map<String, dynamic>)
.copyWith(reference: documentSnapshot.reference)
: UserProfile.empty();
}
I have been investigating and I found there is an issue related to some versions of the analyzer package. I leave it here in case it could be useful for someone in the community (if you use the '0.39.15' or '0.39.16' versions this could be the cause). If that is the case, you can set the override for now inside your pubspec.yaml:
dependency_overrides:
analyzer: '0.39.14'
Also, you should clear all of the caches after:
flutter clean
flutter pub cache repair
flutter pub run build_runner clean

How to convert data from json to List<Object> in Flutter

I need to obtain a list of Articles(a custom object) from a realtime database in Firebase. I first decode my data from a json data type. Then I try to convert it into a list using this line of code:
List<Article> articles = List<Article>.from(articleResponse)
.map((Map model) => Article.fromJson(model))
.toList();
However, this gives a syntax error of "The argument type 'Article Function(Map<dynamic,dynamic>)' can't be assigned to the parameter type 'dynamic Function(Article)'." I have included the code I use to fetch an Article(the custom object) as well as the factory method for the class.
//Method to get articles
Future<List<Article>> fetchArticles() async {
final response = await http.get(
"https://some-server.firebaseio.com/some-url.json");
final articleResponse = json.decode(response.body);
List<Article> articles = List<Article>.from(articleResponse)
.map((Map model) => Article.fromJson(model))
.toList(); // Now we're looping over the response entries (maps of article info) to create Article instances
return articles;
}
\\Factory Method
factory Article.fromJson(Map<String, dynamic> json) {
return Article(
id: json['id'],
title: json['title'],
author: json['author'],
date: json['date'],
imageUrl: json['imageUrl'],
modalities: json['modalities'],
);
}
I make an example with something like a json response.
void main() {
//this is an example like a json response
List<Map<String, dynamic>> articleResponse = [
{
"id":"1",
"name":"test1"
},
{
"id":"2",
"name":"test2"
}
];
List<Article> articles = List<Article>.from(articleResponse.map((Map art)=>Article.fromJson(art)))
.toList();
print('${articles.length} articles in the list!! use to render de ui list');
}
class Article{
String id;
String name;
Article({this.id,this.name});
factory Article.fromJson(Map<String, dynamic> json) {
return Article(
id: json['id'],
name: json['name'],
);
}
}
basically you need to change your method to get articles with this.
//Method to get articles
Future<List<Article>> fetchArticles() async {
final response = await http.get(
"https://some-server.firebaseio.com/some-url.json");
final articleResponse = json.decode(response.body);
List<Article> articles = List<Article>.from(articleResponse.map((Map art)=>Article.fromJson(art)))
.toList(); // Now we're looping over the response entries (maps of article info) to create Article instances
return articles;
}
you can use JsonToDart
this is create a class for parse your complex json data
paste json and get class of model
you can overrride toString in your model like:
#override
String toString() {
return '{
id: $id,
title: $title,
author: $author,
date: $date,
imageUrl: $imageUrl,
modalities: $modalities
}';
}
and override toMap :
Map<String, dynamic> toMap() {
return <String, dynamic>{
'id': id,
'title': title,
'author': author,
'date': date,
'imageUrl': imageUrl,
'modalities': modalities,
};
}
and you can use serialization that. this can help you