How to create an Object Model from Another? - flutter

I want to work with vehicles in my App, which can be of many kinds: trucks, vans, etc.
So, I made a model class of the base elements of the vehicle and some processes to work with the model, something like:
import 'dart:convert';
VehicleModel vehicleModelFromJson(String str) => VehicleModel.fromJson(json.decode(str));
String vehicleModelToJson(VehicleModel data) => json.encode(data.toJson());
class VehicleModel {
VehicleModel({
this.model,
this.licenseplate,
});
String? model;
String? licenseplate;
factory VehicleModel.fromJson(Map<String, dynamic> json) => VehicleModel(
model: json["model"],
licenseplate: json["licenseplate"],
);
Map<String, dynamic> toJson() => {
"model": model,
"licenseplate": licenseplate,
};
}
Now I want to make a new type of vehicle based on the old one. Suppose it is a truck, which has the same properties as a vehicle, and also has a load capacity. I read in Exploring Inheritance and Composition in Dart that you simply have to make a class that inherits properties from the other, however I don't know how to do it
Based on this article, I tried to implement something like this:
import 'dart:convert';
import 'package:pathofmyapp/models/vehicle_model.dart';
TruckModel truckModelFromJson(String str) => TruckModel.fromJson(json.decode(str));
String truckModelToJson(TruckModel data) => json.encode(data.toJson());
class TruckModel extends VehicleModel {
TruckModel({
this.model,
this.licenseplate,
this.loadingcapacity,
}) : super (model: model, licenseplate: licenseplate);
String? loadingcapacity;
factory TruckModel.fromJson(Map<String, dynamic> json) => TruckModel(
model: json["model"],
licenseplate: json["licenseplate"],
loadingcapacity: json["loadingcapacity"],
);
Map<String, dynamic> toJson() => {
"model": model,
"licenseplate": licenseplate,
"loadingcapacity": loadingcapacity,
};
}
But an error appears indicating that the model and licenseplate are not a field in the enclosing class. In toJson function appears a suggestion of type Annotate overridden members.
How can I create a Truck Object based on Vehicle Object?

The model and licenseplate arguments have to be forwarded to the super constructor (which is already done in your code), but therefore the this keyword is misplaced in the TruckModel constructor (both fields are defined in the base class VehicleModel).
TruckModel truckModelFromJson(String str) => TruckModel.fromJson(json.decode(str));
String truckModelToJson(TruckModel data) => json.encode(data.toJson());
class TruckModel extends VehicleModel {
// Remove this keyword from the two fields model and licenseplate
TruckModel({
model,
licenseplate,
this.loadingcapacity,
}) : super (model: model, licenseplate: licenseplate);
String? loadingcapacity;
factory TruckModel.fromJson(Map<String, dynamic> json) => TruckModel(
model: json["model"],
licenseplate: json["licenseplate"],
loadingcapacity: json["loadingcapacity"],
);
// Add #override since you are overwriting a method of the base class
#override
Map<String, dynamic> toJson() => {
"model": model,
"licenseplate": licenseplate,
"loadingcapacity": loadingcapacity,
};
}

Related

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

Generated class from freezed creates duplicate FromJson method

I have a class which I am trying to use with Freezed, Json Serializable, and Hive. After running dart run build_runner build and generating the necessary classes, my compiler gives me the following error:
: Error: Can't use '_$FooBarFromJson' because it is declared more than once.
and
: Error: '_$FooBarFromJson' is already declared in this scope.
part 'foobar.freezed.dart';
part 'foobar.g.dart';
#freezed
#JsonSerializable(explicitToJson: true)
#HiveType(typeId: 0)
class FooBar extends HiveObject with _$FooBar {
factory FooBar({
#HiveField(0) required int baz
}) = _FooBar;
factory FooBar.fromJson(Map<String, dynamic> json) =>
_$FooBarFromJson(json);
}
}
After looking through the generated classes, my foobar.g.dart file contains the following methods:
FooBar _$FooBarFromJson(Map<String, dynamic> json) => FooBar(
baz: json['baz'] as int,
);
Map<String, dynamic> _$FooBarToJson(FooBar instance) =>
<String, dynamic>{
'baz': instance.baz,
};
_$_FooBar _$$_FooBarFromJson(Map<String, dynamic> json) =>
_$_FooBar(
baz: json['baz'] as int,
);
Map<String, dynamic> _$$_FooBarToJson(_$_FooBar instance) =>
<String, dynamic>{
'baz': instance.baz,
};
And my foobar.freezed.dart contains this method:
FooBar _$FooBarFromJson(Map<String, dynamic> json) {
return _FooBar.fromJson(json);
}
I've noticed that other files that get converted only have the methods with the _$$_ prefix in foobar.g.dart, whereas _$FooBarFromJson is being created in both foobar.freezed.dart and foobar.g.dart, which is the cause of the errors. What am I missing here?
According to the following this comment in a freezed issue and as shown in the package readme example, you need to place the #JsonSerializable(explicitToJson: true) inside the class in the following manner:
part 'foobar.freezed.dart';
part 'foobar.g.dart';
#freezed
#HiveType(typeId: 0)
class FooBar extends HiveObject with _$FooBar {
#JsonSerializable(explicitToJson: true) // <~~~ here
factory FooBar({
#HiveField(0) required int baz
}) = _FooBar;
factory FooBar.fromJson(Map<String, dynamic> json) =>
_$FooBarFromJson(json);
}
}
You will notice this will give you the following warning:
The annotation 'JsonSerializable' can only be used on classes
This is a known issue/limitation and the maintainer recommends disabling that specific warning as discussed here.
I believe an alternative approach is to create build.yaml and specify explicitToJson in there but I don't have much knowledge there.

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

Can't (de)serialize nested generic classes with Dart freezed and json_serializable

I got a class First defined as:
#freezed
class First<T> with _$First<T> {
#JsonSerializable(explicitToJson: true)
factory First({
required String a,
#BConverter() required T b,
}) = _First<T>;
factory First.fromJson(Map<String, dynamic> json) => _$FirstFromJson(json);
}
where BConverter() is a JsonConverter.
So far so good, code generation works without issues.
At this point, I need to add class First into other two classes like this:
#freezed
class Second with _$Second {
#JsonSerializable(explicitToJson: true)
factory Second({
required String c,
#Default(<First<SomeClass>>[]) List<First<SomeClass>> d,
}) = _Second;
factory Second.fromJson(Map<String, dynamic> json) => _$SecondFromJson(json);
}
#freezed
class Third with _$Third {
#JsonSerializable(explicitToJson: true)
factory Third({
required String x,
#Default(<First<SomeOtherClass>>[]) List<First<SomeOtherClass>> e,
}) = _Third;
factory Third.fromJson(Map<String, dynamic> json) => _$ThirdFromJson(json);
}
In the latter two cases, the code generation for the json_serializable part fails with the error:
The argument type 'List<First<dynamic>>' can't be assigned to the parameter type 'List<First<SomeClass>>'.
and
The argument type 'List<Second<dynamic>>' can't be assigned to the parameter type 'List<Second<SomeOtherClass>>'.
The error is generated in the .g.dart file in:
_$_Second _$_$_SecondFromJson(Map<String, dynamic> json) {
return _$_Second(
d: (json['d'] as List<dynamic>)
.map((e) => First.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
What am I missing here? What am I doing wrong?

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