Context
I got my desired behavior to work with @JsonKey, but I couldn't get it to work with a JsonConverter. (I think this should be a solution to a related issue, but it doesn't work as I expect.)
Here is my source code:
// my_model.dart
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
part 'my_model.g.dart';
@JsonSerializable(
explicitToJson: true,
converters: [
StringTrimConverter(),
IgnoreEmptyListConverter(),
IgnoreEmptyNullableListConverter(),
],
includeIfNull: false,
)
class MyModel extends Equatable {
final List<int> intList;
final List<String> strList;
final String str;
final String? strOptional;
@JsonKey(toJson: listToJson)
final List<int> intListWithJsonKey;
const MyModel({
this.intList = const [],
this.intListWithJsonKey = const [],
this.strList = const [],
this.str = '',
this.strOptional = '',
});
@override
List<Object?> get props => [
intList,
intListWithJsonKey,
strList,
str,
strOptional,
];
factory MyModel.fromJson(Map<String, dynamic> json) =>
_$MyModelFromJson(json);
Map<String, dynamic> toJson() => _$MyModelToJson(this);
}
List? listToJson(List list) => list.isEmpty ? null : list;
/// Ensure Strings are trimmed when serialized and deserialized.
class StringTrimConverter implements JsonConverter<String, String> {
const StringTrimConverter();
@override
String fromJson(String json) => json.trim();
@override
String toJson(String str) => str.trim();
}
/// Ensure list keys are skipped when serializing JSON
class IgnoreEmptyListConverter implements JsonConverter<List, List?> {
const IgnoreEmptyListConverter();
@override
List fromJson(List? json) => json ?? [];
@override
List? toJson(List list) => list.isEmpty ? null : list;
}
/// Ensure list keys are skipped when serializing JSON
class IgnoreEmptyNullableListConverter implements JsonConverter<List?, List?> {
const IgnoreEmptyNullableListConverter();
@override
List? fromJson(List? json) => json ?? [];
@override
List? toJson(List? list) => (list == null || list.isEmpty) ? null : list;
}
And here is my generated code (after running dart run build_runner build):
// my_model.g.dart
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'my_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
MyModel _$MyModelFromJson(Map<String, dynamic> json) => MyModel(
intList:
(json['intList'] as List<dynamic>?)
?.map((e) => (e as num).toInt())
.toList() ??
const [],
intListWithJsonKey:
(json['intListWithJsonKey'] as List<dynamic>?)
?.map((e) => (e as num).toInt())
.toList() ??
const [],
strList:
(json['strList'] as List<dynamic>?)
?.map((e) => const StringTrimConverter().fromJson(e as String))
.toList() ??
const [],
str: json['str'] == null
? ''
: const StringTrimConverter().fromJson(json['str'] as String),
strOptional:
_$JsonConverterFromJson<String, String>(
json['strOptional'],
const StringTrimConverter().fromJson,
) ??
'',
);
Map<String, dynamic> _$MyModelToJson(MyModel instance) => <String, dynamic>{
'intList': instance.intList,
'strList': instance.strList.map(const StringTrimConverter().toJson).toList(),
'str': const StringTrimConverter().toJson(instance.str),
'strOptional': ?_$JsonConverterToJson<String, String>(
instance.strOptional,
const StringTrimConverter().toJson,
),
'intListWithJsonKey': ?listToJson(instance.intListWithJsonKey),
};
Value? _$JsonConverterFromJson<Json, Value>(
Object? json,
Value? Function(Json json) fromJson,
) => json == null ? null : fromJson(json as Json);
Json? _$JsonConverterToJson<Json, Value>(
Value? value,
Json? Function(Value value) toJson,
) => value == null ? null : toJson(value);
The Good
StringTrimConverter works on a String.
StringTrimConverter works on a List<String>.
includeIfNull: false works on a String?.
@JsonKey(toJson: listToJson) works on an individual List field.
- Note: This is the behavior I want with a
JsonConverter.
The Problem
IgnoreEmptyListConverter is not used at all.
IgnoreEmptyNullableListConverter is not used at all.
- How do I do make this use one of the unused converters? (In other words, how do I do what I'm doing with
@JsonKey without having to put @JsonKey on every List field?)
- Is there something I need to change with one of the converters to make it work?
Other Details
Here's the relevant part of my pubspec.yaml:
name: app
description: "demo"
publish_to: 'none'
version: 0.0.0+1
environment:
sdk: ^3.10.8
dependencies:
flutter:
sdk: flutter
equatable: ^2.0.8
json_annotation: ^4.11.0
meta: ^1.17.0
yaml: ^3.1.2
yaml_writer: ^2.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
build_runner: ^2.10.5
json_serializable: ^6.12.0
Context
I got my desired behavior to work with
@JsonKey, but I couldn't get it to work with aJsonConverter. (I think this should be a solution to a related issue, but it doesn't work as I expect.)Here is my source code:
And here is my generated code (after running
dart run build_runner build):The Good
StringTrimConverterworks on aString.StringTrimConverterworks on aList<String>.includeIfNull: falseworks on aString?.@JsonKey(toJson: listToJson)works on an individualListfield.JsonConverter.The Problem
IgnoreEmptyListConverteris not used at all.IgnoreEmptyNullableListConverteris not used at all.@JsonKeywithout having to put@JsonKeyon everyListfield?)Other Details
Here's the relevant part of my pubspec.yaml: