How to serialize with Map<String, dynamic> using the package built_value - flutter

Does anyone know how can I serialize/deserialize a Map<String, dynamic> instead of String that is the default in the toJson e fromJson methods of the built_value package?
I need to use Firestore and the setData method only accepts a Map for the data.
My current Serializer class has the code below. There's some other plugin or config that I can add to work with the map?
final Serializers serializers =
(_$serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build();
Here are the methods:
String toJson() {
return json.encode(serializers.serializeWith(Comment.serializer, this));
}
static Comment fromJson(String jsonString) {
return serializers.deserializeWith(
Comment.serializer, json.decode(jsonString));
}

Your toJson method should look like:
Map<String, dynamic> toJson() {
return serializers.serializeWith(PlayerModel.serializer, this);
}
That is get rid of the json.encode.
Similarly for fromJson:
static PlayerModel fromJson(Map<String, dynamic> json) {
return serializers.deserializeWith(PlayerModel.serializer, json);
}
Here is a sample PlayerModel I use:
abstract class PlayerModel implements Built<PlayerModel, PlayerModelBuilder> {
#nullable
String get uid;
String get displayName;
PlayerModel._();
factory PlayerModel([void Function(PlayerModelBuilder) updates]) =
_$PlayerModel;
Map<String, dynamic> toJson() {
return serializers.serializeWith(PlayerModel.serializer, this);
}
static PlayerModel fromJson(Map<String, dynamic> json) {
return serializers.deserializeWith(PlayerModel.serializer, json);
}
static Serializer<PlayerModel> get serializer => _$playerModelSerializer;
}

Related

How to do a toJson encode method on Dart/Flutter?

I used the fromJson method to recover a Struct with a List from Json decode http request and receiver it on my class, but now i want to do a reverse, i want to pass the data on my class to my toJson method and send him to a Json encode http POST. Please, i new on Dart/Flutter, someone know how to do this?
import 'dart:convert';
List<Itens> userFromJson(String str) =>
List<Itens>.from(jsonDecode(str).map((x) => Itens.fromJson(x)));
class Coletas {
final int codigo;
final String dataIni;
late String? dataFin;
late String? status;
final List<Itens> itemList;
Coletas(
{
required this.dataIni,
this.dataFin,
this.status,
required this.codigo,
required this.itemList
}
);
factory Coletas.fromJson(Map<String, dynamic> json) {
return Coletas(
dataIni: json['dtData'],
codigo: json['iCodigo'],
itemList: List<Itens>.from(json['stItens'].map((x) => Itens.fromJson(x))),
);
}
Map<String, dynamic> toMap() {
return {
'codigo': codigo,
'dataIni': dataIni,
'dataFin': dataFin,
'status': status
};
}
}
class Itens {
final int? id;
final int codigo;
late int quantidade;
late String? status;
final String codigoEAN;
Itens({
this.id,
this.status,
required this.codigo,
required this.codigoEAN,
required this.quantidade,
});
Map<String, dynamic> toJson(){
return {
'icodigo' : codigo,
'sCodigoBarras': codigoEAN,
'iQtd': quantidade
};
}
factory Itens.fromJson(Map<String, dynamic> json) {
return Itens(
codigo: json['iCodigo'],
codigoEAN: json['sCodigoBarras'],
quantidade: json['iQtd'],
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'status': status,
'codigo': codigo,
'codigoEAN': codigoEAN,
'quantidade': quantidade,
};
}
}
I tried to pass ever item on List separeted so, but not happen i expected.
Map<String, dynamic> toJSon(Coletas value) =>
{
'dtData' : dataIni,
'iCodigo': codigo,
'stItens': [],
};
For a better structure - format and use you can look at the flutter serialization documentation : https://docs.flutter.dev/development/data-and-backend/json.
It explains how to create your model and how to generate them to create fromJson and toJson Model based on the defined data. (https://docs.flutter.dev/development/data-and-backend/json#creating-model-classes-the-json_serializable-way)
It will helps you with your parsing - sending - receiving data.
I think you should assign Coletas as
Map<String, dynamic> toJSon(Coletas value) =>
{
'dtData' : value.dataIni,
'iCodigo': value.codigo,
'stItens': value.itemList,
};

Flutter deserialization of inherited classes with json_serializable

I have problem trying deserialise inherited class with json_serializable package in Dart/Flutter. Here is the code example:
#JsonSerializable(explicitToJson: true)
class Document {
String id = UniqueKey().toString();
String name='';
List<Component> components=[]; //list of Components
Document({required this.components}):super();
Document.empty();
factory Document.fromJson(Map<String, dynamic> json) =>_$DocumentFromJson(json);
Map<String, dynamic> toJson() => _$DocumentToJson(this);
}
#JsonSerializable()
class Component { //Component Base Class
String id = UniqueKey().toString();
String name='';
Component();
factory Component.fromJson(Map<String, dynamic> json) =>_$ComponentFromJson(json);
Map<String, dynamic> toJson() => _$ComponentToJson(this);
}
#JsonSerializable()
class TextComponent extends Component{ //inherited from Component
String text='';
TextComponent():super();
TextComponent.text({required this.text}):super();
factory TextComponent.fromJson(Map<String, dynamic> json) =>_$TextComponentFromJson(json);
#override Map<String, dynamic> toJson() => _$TextComponentToJson(this);
}
And here is the test:
void main() {
//creating json from object
Document d = Document(components:[TextComponent.text(text: 'text')] );
print(d.components[0].runtimeType); //-type is : TextComponent
var json = d.toJson();
//create object from json
var newDoc = Document.fromJson(json);
print(newDoc.components[0].runtimeType); //-type is : Component which is the base class
}
After deserialising the inherited class is downcasted to base class, but i need the inherited class.
Ok, I found this package which does everything:
https://pub.dev/packages/dart_mappable#custom-mappers

json = null in fromJSON method in custom JsonConverter " freezed class with multiple constructors "

I have this class
#freezed
abstract class CartEntity with _$CartEntity {
const factory CartEntity.empty(String status, String message) = _Empty;
const factory CartEntity.notEmpty(int x) = _NotEmpty;
factory CartEntity.fromJson(Map<String, dynamic> json) =>
_$CartEntityFromJson(json);
}
And this converter
class CartEntityConverter
implements JsonConverter<CartEntity, Map<String, dynamic>> {
const CartEntityConverter();
#override
CartEntity fromJson(Map<String, dynamic> json) {
//the problem here
print(json);// null
return _Empty.fromJson(json);
}
#override
Map<String, dynamic> toJson(CartEntity object) {
return object.toJson();
}
}
And this wrapper class
#freezed
abstract class CartEntityWrapper with _$CartEntityWrapper {
const factory CartEntityWrapper(#CartEntityConverter() CartEntity cartEntity) =
CartEntityWrapperData;
factory CartEntityWrapper.fromJson(Map<String, dynamic> json) =>
_$CartEntityWrapperFromJson(json);
}
And iam called
final cartEntity = CartEntityWrapperData.fromJson({'x':'y'});
print(cartEntity);
fromJson method which in CartEntityConverter is always receive null json so what's i made wrong ?
Instead of making yet another converter class that you use directly, you could just add .fromJsonA method in the main class.
It will looks like this one:
#freezed
abstract class CartEntity with _$CartEntity {
const factory CartEntity.empty(String status, String message) = _Empty;
const factory CartEntity.notEmpty(int x) = _NotEmpty;
factory CartEntity.fromJson(Map<String, dynamic> json) =>
_$CartEntityFromJson(json);
factory CartEntity.fromJsonA(Map<String, dynamic> json) {
if (/*condition for .empty constructor*/) {
return _Empty.fromJson(json);
} else if (/*condition for .notEmpty constructor*/) {
return _NotEmpty.fromJson(json);
} else {
throw Exception('Could not determine the constructor for mapping from JSON');
}
}
}
solved by using
final cartEntity = CartEntityConverter().fromJson({'x':'y'});
print(cartEntity);
instead of
final cartEntity = CartEntityWrapperData.fromJson({'x':'y'});
print(cartEntity);
documentation have a lack at this point i tried random stuffs to make it work

flutter dart JsonSerializable with inherited class

I have the following two classes where one is extending from the other like this :
#JsonSerializable(nullable: true)
class Response {
final String responseCode;
final String responseMessage;
final String errorLog;
Response({this.errorLog, this.responseCode, this.responseMessage});
factory Response.fromJson(Map<String, dynamic> json) =>
_$ResponseFromJson(json);
}
.........................................................
#JsonSerializable(nullable: false)
class Verify extends Response {
Data data;
Verify({
this.data,
});
factory Verify.fromJson(Map<String, dynamic> json) => _$VerifyFromJson(json);
Map<String, dynamic> toJson() => _$VerifyToJson(this);
}
and whenever I'm trying to read response class properties from Verify class, it's always null.
so please how to achieve this?
this one I have solved by passing the parameters to super in verify class constructor like this
#JsonSerializable()
class VerifyResponse extends Response {
Data data;
VerifyResponse({
this.data,
String responseCode,
String responseMessage,
}) : super(responseCode: responseCode, responseMessage: responseMessage);
factory VerifyResponse.fromJson(Map<String, dynamic> json) =>
_$VerifyResponseFromJson(json);
Map<String, dynamic> toJson() => _$VerifyResponseToJson(this);
}
and for the response class it remains the same
#JsonSerializable()
class Response {
final String responseCode;
final String responseMessage;
Response({this.responseCode, this.responseMessage});
factory Response.fromJson(Map<String, dynamic> json) =>
_$ResponseFromJson(json);
}
it's a bit annoying but it's what it's.
You should remove 'final' keyword from Response Class
#JsonSerializable(nullable: true)
class Response {
String responseCode;
String responseMessage;
String errorLog;
Response({this.errorLog, this.responseCode, this.responseMessage});
factory Response.fromJson(Map<String, dynamic> json) =>
_$ResponseFromJson(json);
}
It worked by adding super(); explicitly to the child class's constructor.
#JsonSerializable()
class VerifyResponse extends Response {
Data data;
VerifyResponse({
this.data,
String responseCode,
String responseMessage,
//No need to list all parent class properties
}) : super();
factory VerifyResponse.fromJson(Map<String, dynamic> json) =>
_$VerifyResponseFromJson(json);
Map<String, dynamic> toJson() => _$VerifyResponseToJson(this);
}

Flutter return parsing dynamic object from service

I have created multiple models and using json_serialization
Example Company and Employee and many more.
import 'package:json_annotation/json_annotation.dart';
part 'company.g.dart';
#JsonSerializable()
class Company {
Company({this.id, this.name});
String id;
String name;
factory Company.fromJson(Map<String, dynamic> json) => _$CompanyFromJson(json);
Map<String, dynamic> toJson() => _$CompanyToJson(this);
}
import 'package:json_annotation/json_annotation.dart';
part 'employee.g.dart';
#JsonSerializable()
class Employee {
Employee({this.id, this.name, this.email, this.phone, this.photo});
String id;
String name;
String email;
String phone;
String photo;
factory Employee.fromJson(Map<String, dynamic> json) => _$EmployeeFromJson(json);
Map<String, dynamic> toJson() => _$EmployeeToJson(this);
}
Now I want to create a reusable service that can return Stream with type Company, Employee or other types.
I'm using Firebase so the return type is Map.
example service class
class BaseService<T> {
final String collection;
CollectionReference _collectionRef;
FirebaseBase({#required this.collection}) {
_collectionRef = Firestore.instance.collection(collection);
}
Stream<List<T>> find() {
return inColRef.snapshots().map((list) {
return list.documents.map((doc) {
final Map<String, dynamic> data = doc.data;
data['id'] = doc.documentID;
return data;
}).toList();
});
}
}
How do I convert the return data (Map) to a type Company or Employee.
Those class can use the factory of fromJson(data).
But I can't return a T.fromJson(data).
I would like to get
Stream<List<Company>> companies = ServiceBase('companies').find();
Stream<List<Employee>> employees = ServiceBase('employee').find();
This is how you would accomplish the generic assignment using your current code:
class BaseService<T> {
final String collection;
CollectionReference _collectionRef;
FirebaseBase({#required this.collection}) {
_collectionRef = Firestore.instance.collection(collection);
}
Stream<List<T>> find() {
return inColRef.snapshots().map((list) {
return list.documents.map((doc) {
final Map<String, dynamic> data = doc.data;
data['id'] = doc.documentID;
return _build(data) as T;
}).toList();
});
}
dynamic _build(final Map<String, dynamic> data) {
if(collection == 'companies') {
return Company.fromJson(data);
} else if(collection == 'employee') {
return Employee.fromJson(data);
}
... throw if invalid collection passed ? ...
}
}
This would be called as so:
Stream<List<Company>> companies = BaseService<Company>('companies').find();
Stream<List<Employee>> employees = BaseService<Employee>('employee').find();
I would recommend that you should a different object structure with the template method pattern such as:
class CompanyService extends BaseService<Company> {
CompanyService() : super('companies');
Company build(final Map<String, dynamic> data) => Company.fromJson(data);
}
class EmployeeService extends BaseService<Employee> {
EmployeeService() : super('employee');
Employee build(final Map<String, dynamic> data) => Employee.fromJson(data);
}
abstract class BaseService<T> {
final String collection;
CollectionReference _collectionRef;
BaseService(this.collection) {
_collectionRef = Firestore.instance.collection(collection);
}
T build(final Map<String, dynamic> data);
Stream<List<T>> find() {
return inColRef.snapshots().map((list) {
return list.documents.map((doc) {
final Map<String, dynamic> data = doc.data;
data['id'] = doc.documentID;
return build(data);
}).toList();
});
}
}
which would result in the calling code looking something like this:
Stream<List<Company>> companies = CompanyService().find();
Stream<List<Employee>> employees = EmployeeService().find();