Skip to content

Commit

Permalink
Add option to drop nullable values in "toJson"
Browse files Browse the repository at this point in the history
  • Loading branch information
spideythewebhead committed Jun 29, 2024
1 parent d35006b commit eab91fa
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 3 deletions.
6 changes: 6 additions & 0 deletions examples/file_generation_mode/data_class_plugin_options.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# default config
auto_delete_code_from_annotation: false

json:
to_json:
options_config:
drop_null_values:
default: true

data_class:
options_config:
# default config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class _$UserImpl extends User {
return <String, dynamic>{
'id': id,
'username': username,
'email': email,
if (email != null) 'email': email,
'isVerified': isVerified,
};
}
Expand Down
15 changes: 14 additions & 1 deletion package/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ To create a custom configuration you need to add a file named `data_class_plugin
```

When `auto_delete_code_from_annotation` is `true` => `toJson` will be removed because is set as `false` in the `@DataClass`.

When `auto_delete_code_from_annotation` is `false` => `toJson` will be kept as it is even when `toJson` is set as `false` in the `@DataClass`. This allows to create custom `fromJson/toJson` implementations.

1. `json`
Expand Down Expand Up @@ -333,6 +333,19 @@ json:
- "a/glob/here"
- "another/glob/here"
# Allows to configure "toJson" code generation
# If no config is provided, null values will be dropped by default
to_json:
options_config:
drop_null_values:
default: boolean # default value is there is no match in enabled or disabled lists
enabled: # list of globs
- "a/glob/here"
- "another/glob/here"
disabled: # list of globs
- "a/glob/here"
- "another/glob/here"
data_class:
options_config:
# For each of the provided methods you can provide a configuration
Expand Down
3 changes: 3 additions & 0 deletions package/lib/src/backend/code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ class DataClassPluginGenerator extends TachyonPluginCodeGenerator {
jsonKeyNameConventionGetter: jsonKeyNameConventionGetter,
classDeclarationFinder: declarationFinder.findClassOrEnum,
logger: logger,
dropNullValues: pluginOptions.json.toJson.effectiveDropNullValues(targetFileRelativePath),
).execute();
}

Expand Down Expand Up @@ -420,6 +421,8 @@ class DataClassPluginGenerator extends TachyonPluginCodeGenerator {
toJsonUnionKey: unionAnnotationValueExtractor.getString('unionJsonKey'),
classDeclarationFinder: declarationFinder.findClassOrEnum,
logger: logger,
dropNullValues:
pluginOptions.json.toJson.effectiveDropNullValues(targetFileRelativePath),
).execute();
}

Expand Down
16 changes: 15 additions & 1 deletion package/lib/src/backend/core/generators/to_json.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ class ToJsonGenerator implements Generator {
required final List<DeclarationInfo> fields,
required final JsonKeyNameConventionGetter jsonKeyNameConventionGetter,
required final ClassOrEnumDeclarationFinder classDeclarationFinder,
required final bool dropNullValues,
final String? toJsonUnionKey,
required final Logger logger,
}) : _codeWriter = codeWriter,
_fields = fields,
_jsonKeyNameConventionGetter = jsonKeyNameConventionGetter,
_classDeclarationFinder = classDeclarationFinder,
_dropNullValues = dropNullValues,
_toJsonUnionKey = toJsonUnionKey,
_logger = logger;

Expand All @@ -24,6 +26,7 @@ class ToJsonGenerator implements Generator {
final JsonKeyNameConventionGetter _jsonKeyNameConventionGetter;
final ClassOrEnumDeclarationFinder _classDeclarationFinder;
final String? _toJsonUnionKey;
final bool _dropNullValues;
final Logger _logger;

@override
Expand Down Expand Up @@ -82,6 +85,10 @@ class ToJsonGenerator implements Generator {
jsonKeyNameConvention.transform(fieldName.escapeDollarSign());
final TachyonDartType dartType = field.type?.customDartType ?? TachyonDartType.dynamic;

if (_dropNullValues && dartType.isNullable) {
_codeWriter.writeln('if ($fieldName != null)');
}

_codeWriter.write("'$jsonFieldName': ");

if (customJsonConverter != null) {
Expand Down Expand Up @@ -112,6 +119,9 @@ class ToJsonGenerator implements Generator {
void _writeNullableParsingPrefix({
required final String parentVariableName,
}) {
if (_dropNullValues) {
return;
}
_codeWriter.write('$parentVariableName == null ? null : ');
}

Expand Down Expand Up @@ -174,7 +184,11 @@ class ToJsonGenerator implements Generator {

if (typeDeclarationNode is ClassDeclaration && typeDeclarationNode.hasMethod('toJson') ||
typeDeclarationNode is EnumDeclaration && typeDeclarationNode.hasMethod('toJson')) {
final String accessOperator = dartType.isNullable ? '?.' : '.';
final String accessOperator = switch (dartType.isNullable) {
true when _dropNullValues => '!.',
true => '?.',
false => '.',
};
_codeWriter.writeln('$parentVariableName${accessOperator}toJson(),');
return;
}
Expand Down
4 changes: 4 additions & 0 deletions package/lib/src/options/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ extension OptionsGlobMatch on Map<String, OptionConfig> {
return _effectiveValue('when', filePath, defaultValue);
}

bool effectiveToJsonDropNullValues({required String filePath, required bool defaultValue}) {
return _effectiveValue('drop_null_values', filePath, defaultValue);
}

bool _effectiveValue(
String method,
String filePath,
Expand Down
26 changes: 26 additions & 0 deletions package/lib/src/options/json_options.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'package:data_class_plugin/data_class_plugin.dart';
import 'package:data_class_plugin/src/options/data_class_plugin_options.dart';
import 'package:data_class_plugin/src/options/extensions.dart';
import 'package:data_class_plugin/src/options/options_config.dart';

part 'json_options.gen.dart';

Expand All @@ -10,6 +13,7 @@ abstract class JsonOptions {
const factory JsonOptions({
String? keyNameConvention,
Map<String, List<String>> nameConventionGlobs,
ToJsonOptions toJson,
}) = _$JsonOptionsImpl;

/// Creates an instance of [JsonOptions] from [json]
Expand All @@ -20,4 +24,26 @@ abstract class JsonOptions {
@JsonKey(name: 'key_name_conventions')
@DefaultValue(<String, List<String>>{})
Map<String, List<String>> get nameConventionGlobs;

@DefaultValue(ToJsonOptions())
ToJsonOptions get toJson;
}

@DataClass()
abstract class ToJsonOptions {
const ToJsonOptions.ctor();

/// Creates an instance of [ToJsonOptions] from [json]
factory ToJsonOptions.fromJson(Map<dynamic, dynamic> json) = _$ToJsonOptionsImpl.fromJson;

/// Default constructor
const factory ToJsonOptions({
Map<String, OptionConfig> optionsConfig,
}) = _$ToJsonOptionsImpl;

@DefaultValue(<String, OptionConfig>{})
Map<String, OptionConfig> get optionsConfig;

bool effectiveDropNullValues(String filePath) =>
optionsConfig.effectiveToJsonDropNullValues(filePath: filePath, defaultValue: true);
}
33 changes: 33 additions & 0 deletions package/lib/src/options/json_options.gen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class _$JsonOptionsImpl extends JsonOptions {
const _$JsonOptionsImpl({
this.keyNameConvention,
Map<String, List<String>> nameConventionGlobs = const <String, List<String>>{},
this.toJson = const ToJsonOptions(),
}) : _nameConventionGlobs = nameConventionGlobs,
super.ctor();

Expand All @@ -20,6 +21,9 @@ class _$JsonOptionsImpl extends JsonOptions {
Map<String, List<String>>.unmodifiable(_nameConventionGlobs);
final Map<String, List<String>> _nameConventionGlobs;

@override
final ToJsonOptions toJson;

factory _$JsonOptionsImpl.fromJson(Map<dynamic, dynamic> json) {
return _$JsonOptionsImpl(
keyNameConvention: json['key_name_convention'] as String?,
Expand All @@ -32,9 +36,38 @@ class _$JsonOptionsImpl extends JsonOptions {
for (final dynamic i1 in (e0.value as List<dynamic>)) i1 as String,
],
},
toJson:
json['to_json'] == null ? const ToJsonOptions() : ToJsonOptions.fromJson(json['to_json']),
);
}

@override
Type get runtimeType => JsonOptions;
}

class _$ToJsonOptionsImpl extends ToJsonOptions {
const _$ToJsonOptionsImpl({
Map<String, OptionConfig> optionsConfig = const <String, OptionConfig>{},
}) : _optionsConfig = optionsConfig,
super.ctor();

@override
Map<String, OptionConfig> get optionsConfig =>
Map<String, OptionConfig>.unmodifiable(_optionsConfig);
final Map<String, OptionConfig> _optionsConfig;

factory _$ToJsonOptionsImpl.fromJson(Map<dynamic, dynamic> json) {
return _$ToJsonOptionsImpl(
optionsConfig: json['options_config'] == null
? const <String, OptionConfig>{}
: <String, OptionConfig>{
for (final MapEntry<dynamic, dynamic> e0
in (json['options_config'] as Map<dynamic, dynamic>).entries)
e0.key as String: OptionConfig.fromJson(e0.value),
},
);
}

@override
Type get runtimeType => ToJsonOptions;
}

0 comments on commit eab91fa

Please sign in to comment.