google / google/json_serializable.dart

Unable to skip empty List when serializing JSON with JsonConverter

Open
#1,569 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Dart
Stars
1.6k
Forks
461
Avg merge
45m
Merged PRs (30d)
1

Description

### 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](https://github.com/google/json_serializable.dart/issues/1369), but it doesn't work as I expect.)

Here is my source code:
```dart
// 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 intList;
final List strList;
final String str;
final String? strOptional;

@JsonKey(toJson: listToJson)
final List intListWithJsonKey;

const MyModel({
this.intList = const [],
this.intListWithJsonKey = const [],
this.strList = const [],
this.str = '',
this.strOptional = '',
});

@override
List get props => [
intList,
intListWithJsonKey,
strList,
str,
strOptional,
];

factory MyModel.fromJson(Map json) =>
_$MyModelFromJson(json);

Map toJson() => _$MyModelToJson(this);
}

List? listToJson(List list) => list.isEmpty ? null : list;

/// Ensure Strings are trimmed when serialized and deserialized.
class StringTrimConverter implements JsonConverter {
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 {
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 {
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`):
```dart
// my_model.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'my_model.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

MyModel _$MyModelFromJson(Map json) => MyModel(
intList:
(json['intList'] as List?)
?.map((e) => (e as num).toInt())
.toList() ??
const [],
intListWithJsonKey:
(json['intListWithJsonKey'] as List?)
?.map((e) => (e as num).toInt())
.toList() ??
const [],
strList:
(json['strList'] as List?)
?.map((e) => const StringTrimConverter().fromJson(e as String))
.toList() ??
const [],
str: json['str'] == null
? ''
: const StringTrimConverter().fromJson(json['str'] as String),
strOptional:
_$JsonConverterFromJson(
json['strOptional'],
const StringTrimConverter().fromJson,
) ??
'',
);

Map _$MyModelToJson(MyModel instance) => {
'intList': instance.intList,
'strList': instance.strList.map(const StringTrimConverter().toJson).toList(),
'str': const StringTrimConverter().toJson(instance.str),
'strOptional': ?_$JsonConverterToJson(
instance.strOptional,
const StringTrimConverter().toJson,
),
'intListWithJsonKey': ?listToJson(instance.intListWithJsonKey),
};

Value? _$JsonConverterFromJson(
Object? json,
Value? Function(Json json) fromJson,
) => json == null ? null : fromJson(json as Json);

Json? _$JsonConverterToJson(
Value? value,
Json? Function(Value value) toJson,
) => value == null ? null : toJson(value);
```

### The Good
- `StringTrimConverter` works on a `String`.
- `StringTrimConverter` works on a `List`.
- `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.

1. 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?)
2. 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:

```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
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.