Generated class from freezed creates duplicate FromJson method - flutter

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.

Related

Using dart's json_serializable package with custom properties [duplicate]

This question already has an answer here:
Complex models with json_serializable - List<objects> not converting to map
(1 answer)
Closed 5 months ago.
Background
I'm trying to use the dart json_serializable package to encode/decode a custom type I'm writing/reading with Google Cloud Firestore, and I'm having an issue with the custom type having properties that are also custom types.
I'm storing a Habit which has two custom properties:
HabitFrequency is a combination of an integer, and an enum frequencyType that I want to store together as a map in Firestore.
HabitCompletion is a list of the type HabitCompletion, which contains a date, completion status, and an optional string value/numeric value. I want this to be stored as an array of maps.
Issue
Whenever I try to save, I get this error:
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument: Instance of 'HabitFrequency'
I have the top-level class and both custom classes used as properties (HabitFrequency and HabitCompletion) listed as JsonSerializable, and they have the appropriate fromJson/toJson methods specified.
Question
Is there a different way I need to set these up before I run build_runner build to generate the right code to serialize these properly?
Code Examples
Here is my top-level class:
#JsonSerializable()
class Habit {
String? id;
String title;
#JsonKey(name: 'frequency')
HabitFrequency frequency;
List<HabitCompletion>? completions;
Habit({
this.id,
required this.title,
required this.frequency,
this.completions,
});
/// Connect the generated [_$PersonFromJson] function to the `fromJson`
/// factory.
factory Habit.fromJson(Map<String, dynamic> json) => _$HabitFromJson(json);
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
Map<String, dynamic> toJson() => _$HabitToJson(this);
}
}
The Habit Frequency:
#JsonSerializable()
class HabitFrequency {
int amount;
HabitFrequencyType frequencyType;
HabitFrequency(this.amount, this.frequencyType);
/// Connect the generated [_$HabitFrequencyFromJson] function to the `fromJson`
/// factory.
factory HabitFrequency.fromJson(Map<String, dynamic> json) =>
_$HabitFrequencyFromJson(json);
/// Connect the generated [_$HabitFrequencyToJson] function to the `toJson` method.
Map<String, dynamic> toJson() => _$HabitFrequencyToJson(this);
}
And the Habit completion:
#JsonSerializable()
class HabitCompletion {
DateTime timestamp;
bool completion;
String? stringValue;
double? numericValue;
HabitCompletion(
{required this.timestamp,
required this.completion,
this.stringValue,
this.numericValue});
/// Connect the generated [_$PersonFromJson] function to the `fromJson`
/// factory.
factory HabitCompletion.fromJson(Map<String, dynamic> json) =>
_$HabitCompletionFromJson(json);
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
Map<String, dynamic> toJson() => _$HabitCompletionToJson(this);
}
They each have one property that is just a simple enumeration.
If you want to see more of the code, here's a gist containing the file for this type (and the custom types used for the properties in questions), as well as the auto-generated code coming from json_serializable.
this can be resolved by setting explicitToJson on the JsonSerializable annotation for the Habit class as mentioned in your gist
#JsonSerializable(explicitToJson: true)
class Habit{
....
}
u should be getting syntax as some properties are not defined but u are using them in the constructor?
#JsonSerializable()
class Habit {
String? id;
String title;
#JsonKey(name: 'frequency')
HabitFrequency frequency;
List<HabitCompletion>? completions;
Habit({
this.id,
required this.title,
// not defined??????
this.description,
// not defined?????? //etc...
required this.userId,
this.attachedDashboardId,
this.attachedGoalId,
required this.startDate,
this.endDate,
this.isActive = true,
this.positivity = HabitPositivity.positive,
this.frequency,
this.completions,
});
/// Connect the generated [_$PersonFromJson] function to the `fromJson`
/// factory.
factory Habit.fromJson(Map<String, dynamic> json) => _$HabitFromJson(json);
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
Map<String, dynamic> toJson() => _$HabitToJson(this);
}
}

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

How to create an Object Model from Another?

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

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 deserialisation using generics error

I am trying to deserialise and serialise a generic class in flutter using build_runner. The code I have written is as below:
import 'package:json_annotation/json_annotation.dart';
import 'api_response_message_model.dart';
part 'api_response_base_model.g.dart';
#JsonSerializable()
class ApiResponseBaseModel<T> {
#JsonKey(fromJson: _dataFromJson, toJson: _dataToJson)
T data;
ApiResponseMessageModel message;
ApiResponseBaseModel({this.data, this.message});
factory ApiResponseBaseModel.fromJson(Map<String, dynamic> json) =>
_$ApiResponseBaseModelFromJson(json);
T _dataFromJson<T>(Map<String, dynamic> input) =>
input['value'] as T;
Map<String, dynamic> _dataToJson<T>(T input) =>
{'value': input};
}
I am getting the error as below when running command flutter packages pub run build_runner build --delete-conflicting-outputs
Could not generate `fromJson` code for `data`.
None of the provided `TypeHelper` instances support the defined type.
package:premier_app/models/api_response_base_model.dart:10:5
╷
10│ T data;
│ ^^^^
╵
Please help me identify the problem and know the correct solution.
I also did tried the method mentioned here but no luck in removing the error - https://github.com/dart-lang/json_serializable/blob/7b40e9b04805bf921e4cebb87ec4ad7b8e1a2d29/json_serializable/test/generic_files/generic_class.dart#L20-L21
Please move the _dataFromJson & _dataToJson methods outside the scope of class ApiResponseBaseModel<T>.
Example:
import 'package:json_annotation/json_annotation.dart';
import 'api_response_message_model.dart';
part 'api_response_base_model.g.dart';
#JsonSerializable()
class ApiResponseBaseModel<T> {
#JsonKey(fromJson: _dataFromJson, toJson: _dataToJson)
T data;
ApiResponseMessageModel message;
ApiResponseBaseModel({this.data, this.message});
factory ApiResponseBaseModel.fromJson(Map<String, dynamic> json) =>
_$ApiResponseBaseModelFromJson(json);
}
T _dataFromJson<T>(Map<String, dynamic> input) =>
input['value'] as T;
Map<String, dynamic> _dataToJson<T>(T input) =>
{'value': input};
I did try out myself, works!
Generated methods look like:
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'api_response_base_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
ApiResponseBaseModel<T> _$ApiResponseBaseModelFromJson<T>(
Map<String, dynamic> json) {
return ApiResponseBaseModel<T>(
data: _dataFromJson(json['data'] as Map<String, dynamic>),
message: json['message'],
);
}
Map<String, dynamic> _$ApiResponseBaseModelToJson<T>(
ApiResponseBaseModel<T> instance) =>
<String, dynamic>{
'data': _dataToJson(instance.data),
'message': instance.message,
};