Maqcel
Maqcel

Reputation: 509

Json serializer with freezed - firestore comunication

I'm struggling to implement the cart feature inside my project, I'm trying to pass the serialized JSON (generated with freezed package) but I've got a problem with the List of CartItem. Error: Invalid argument (dartObject): Could not convert: Instance of '_$36_CartItem'

My models for cart:

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
class User with _$User {
  factory User({
    required final String id,
    required final String email,
    required List<CartItem> cart,
  }) = _User;

  const User._();

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

Product:

part 'product.freezed.dart';
part 'product.g.dart';

@freezed
class Product with _$Product {
  factory Product({
    required final String id, 
    required final String name, 
    required final double price, 
    required final bool isBestseller, 
    required final String description, 
    required final String imageUrl, 
  }) = _Product;

  const Product._();

  factory Product.fromJson(Map<String, dynamic> json) =>
      _$ProductFromJson(json);
}

CartItem:

part 'cart_item.freezed.dart';
part 'cart_item.g.dart';

@freezed
class CartItem with _$CartItem {
  factory CartItem({
    required final Product product,
    required int quantity,
  }) = _CartItem;

  const CartItem._();

  factory CartItem.fromJson(Map<String, dynamic> json) =>
      _$CartItemFromJson(json);
}

Method for passing data to firestore:

Future<void> firebaseCreateUser(firebase_auth.User user) async {
    CollectionReference users = FirebaseFirestore.instance.collection('users');
    List<CartItem> test = [
      CartItem(product: MockData.mockProducts.elementAt(0), quantity: 2),
      CartItem(product: MockData.mockProducts.elementAt(1), quantity: 2)
    ];
    return users
        .doc(user.uid)
        .set(User(cart: test, email: user.email!, id: user.uid).toJson())
        .catchError((e) => print('Failed to add user: $e'));
  }

Generated toJson inside .g.dart (all generated files has same pattern for toJson):

Map<String, dynamic> _$_$_UserToJson(_$_User instance) => <String, dynamic>{
      'id': instance.id,
      'email': instance.email,
      'cart': instance.cart,
    };

I think that the problem lies in the CartItem in my opinion User.toJson() should call all toJson methods down in the object but I'm implementing it the first time so I don't really know.

Should I write my own toJson in User and then call build runner and freezed will regenerate new toJson?

Upvotes: 3

Views: 1797

Answers (2)

taki fouhal
taki fouhal

Reputation: 86

cart items objects need to be converted to Maps. in order to serialize nested objects, you have to change explicit_to_json to true in your build.yaml file (create one if it does not exist)

https://github.com/google/json_serializable.dart/tree/master/json_serializable#build-configuration

Upvotes: 1

Arnaud Delubac
Arnaud Delubac

Reputation: 839

You will have to use the decorator @JsonKey(fromJson: User.cartItemFromJson, toJson: User.cartItemToJson) for the cart property and create those 2 static methods inside your User class:

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
class User with _$User {
  factory User({
    required final String id,
    required final String email,
    @JsonKey(fromJson: User.cartItemFromJson, toJson: User.cartItemToJson)
    required List<CartItem> cart,
  }) = _User;

  const User._();

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

  static cartItemFromJson(List<dynamic?> listOfItem) => listOfItem.isEmpty ? [] 
    : listOfItem.map((item) => CartItem.fromJson(item)).toList();

  static cartItemToJson(List<CartItem?> items) => items.isEmpty ? []
    : items.map((item) => CartItem.toJson()).toList();
}

Or you can create a class which implements JsonConverter and use this class as a decorator, all the info is in the freezed documentation: https://pub.dev/packages/freezed#fromjsontojson

Upvotes: 6

Related Questions