How to delegate a call to a method in json_serialization? - flutter

I've two classes Foo and Bar:
#JsonSerializable()
class Foo {
final Bar bar;
Foo(this.bar);
factory Foo.fromJson(Map<String, dynamic> json) => _$FooFromJson(json);
Map<String, dynamic> toJson() => _$FooToJson(this);
}
#JsonSerializable()
class Bar {
final String s;
Bar(this.s);
factory Bar.fromJson(Map<String, dynamic> json) => _$BarFromJson(json);
Map<String, dynamic> toJson() => _$BarToJson(this);
}
Once the code is generated, I use:
final bar = Bar('hi');
final foo = Foo(bar);
print(foo.toJson());
The above line prints
{bar: Instance of 'Bar'}
but I want it to print
{bar: "hi"}
The one way is to override toString method in the Bar class but I don't think it's a good way when using json_serializable for code generation. I think there's trivial property I am missing.

For having custom class inside another class you need to use explicitToJson: true in your target class.
#JsonSerializable(explicitToJson: true)
class Foo {
final Bar bar;
Foo(this.bar);
factory Foo.fromJson(Map<String, dynamic> json) => _$FooFromJson(json);
Map<String, dynamic> toJson() => _$FooToJson(this);
}

Related

flutter map was called on a null value

I try to get data from api. CallListDto class is empty according to user type. So, sometime this class is empty sometimes it has data. I populate dropdown menu items with this class.
My problem is, when this class is empty i got error. How can i solve this.
My model class is below
Login loginFromJson(String str) => Login.fromJson(json.decode(str));
String loginToJson(Login data) => json.encode(data.toJson());
class Login {
Login({
required this.token,
required this.callListDto,
});
String token;
List<CallListDto> callListDto;
factory Login.fromJson(Map<String, dynamic> json) => Login(
token: json["token"],
callListDto: List<CallListDto>.from(
json["callListDto"].map((x) => CallListDto.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"token": token,
"callListDto": List<dynamic>.from(callListDto.map((x) => x.toJson())),
};
}
class CallListDto {
CallListDto({
required this.callId,
required this.stationCode,
required this.callType,
});
int callId;
String stationCode;
int callType;
factory CallListDto.fromJson(Map<String, dynamic> json) => CallListDto(
callId: json["callID"],
stationCode: json["stationCode"],
callType: json["callType"],
);
Map<String, dynamic> toJson() => {
"callID": callId,
"stationCode": stationCode,
"callType": callType,
};
}
Before calling the model class check that str is not empty, for example
if (dataObject.isEmpty) return;

How to use private constructor in json_serializable

I'm using a private constructor in my class, but the code generation fails with
The class Foo has no default constructor.
I'm using latest json_serializable: version i.e. 6.1.5:
#JsonSerializable()
class Foo {
final int count;
Foo._(this.count);
factory Foo.fromJson(Map<String, dynamic> json) => _$Foo._FromJson(json);
}
What am I doing wrong?
You can use #JsonSerializable(constructor: '_') which is introduced in the 4.2.0-dev of the JsonSerializable.
This will allow you to field to specify an alternative constructor to invoke when creating a fromJson helper.
For example:
import 'package:json_annotation/json_annotation.dart';
part 'foo.g.dart';
#JsonSerializable(constructor: '_')
class Foo {
final int count;
Foo._(this.count);
factory Foo.fromJson(Map<String, dynamic> json) => _$FooFromJson(json);
}
Now here, instead of using fromJson like this _$Foo._FromJson(json), use it as _$FooFromJson(json)
Since you declared your constructor with _, there is no default constructor for your class. To fix your issue remove the _.
#JsonSerializable()
class Foo {
final int count;
const Foo(this.count);
factory Foo.fromJson(Map<String, dynamic> json) => _$Foo._FromJson(json);
}
if you need to json_serializable: generate is for you, You have to define the default constructor. but you can mack this trick:
first do this and run build_runner:
#JsonSerializable()
class Foo {
final int count;
Foo(this.count);
factory Foo.fromJson(Map<String, dynamic> json) => _$FooFromJson(json);
}
then change it to this:
#JsonSerializable()
class Foo {
final int count;
Foo._(this.count);
factory Foo.fromJson(Map<String, dynamic> json) => _$FooFromJson(json);
}
and go to _.g.dart and mack call the Private constructor:
Foo _$FooFromJson(Map<String, dynamic> json) => Foo._(
json['count'] as int,
);
Map<String, dynamic> _$FooToJson(Foo instance) => <String, dynamic>{
'count': instance.count,
};

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

How to use Class in variable?

I want to convert fetched data as below, but I got error and emulator shutdown!
What can I do?
Map<String, dynamic> responseClassMap = {
'$ResponseCompany': ResponseCompany,//ResponseCompany is class
'$ResponseCompanyDetail': ResponseCompanyDetail, //ResponseCompanyDetail is class
};
for (var item in responseClassMap.entries) {
if (className == item.key) {
result = responseData.map((data) => item.value.fromJson(data)).toList();
}
}
Here is class ResponseCompany.dart
#JsonSerializable()
class ResponseCompany {
final num sales, ...;
...
factory ResponseCompany.fromJson(Map<String, dynamic> json) => _$ResponseCompanyFromJson(json);
...
Here is ResponseCompany.g.dart
ResponseCompany _$ResponseCompanyFromJson(Map<String, dynamic> json) {
return ResponseCompany(
);
...
}
IMHO item.value.fromJson will not work. Since fromJson is a factory constructor, and in dart's rule, one cannot call factory constructor for a type stored in a variable. (Indeed, the problem is hidden because you create a Map<string, dynamic> and dart allow everything to be called on dynamic at compile time.)
For your specific case, you can do
Map<String, dynamic> map = {
'$ResponseCompany': (d)=>ResponseCompany.fromJson(d),//ResponseCompany is class
'$ResponseCompanyDetail': (d)=>ResponseCompanyDetail.fromJson(d), //ResponseCompanyDetail is class
};
for (var item in map.entries) {
if (className == item.key) {
result = responseData.map((data) => item.value(data)).toList();
}
}

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);
}