From 7d71de0605a7ed473a65be34401087a1e9d84ca2 Mon Sep 17 00:00:00 2001 From: hacker1024 Date: Mon, 18 Jul 2022 20:03:19 +1000 Subject: [PATCH] Allow extra non-serializable fields to be passed into fromJson and ignored in toJson Document JsonKey.extra Add extra key to KeyConfig class Fixes --- json_annotation/lib/src/json_key.dart | 10 +++++++ json_serializable/lib/src/decode_helper.dart | 28 +++++++++++++++---- .../lib/src/generator_helper.dart | 21 +++++++++----- json_serializable/lib/src/json_key_utils.dart | 3 ++ .../lib/src/type_helpers/config_types.dart | 3 ++ 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/json_annotation/lib/src/json_key.dart b/json_annotation/lib/src/json_key.dart index 96967c2a9..9489020e5 100644 --- a/json_annotation/lib/src/json_key.dart +++ b/json_annotation/lib/src/json_key.dart @@ -112,6 +112,15 @@ class JsonKey { /// valid on a nullable enum field. final Enum? unknownEnumValue; + /// `true` if this field should not be serialized. + /// + /// Fields annotated with [extra] set to true will not be included in JSON + /// output, and their values may be passed in as additional arguments + /// alongside JSON data in the deserialization function. + /// + /// If `null` (the default) or false, this argument has no effect. + final bool? extra; + /// Creates a new [JsonKey] instance. /// /// Only required when the default behavior is not desired. @@ -127,6 +136,7 @@ class JsonKey { this.required, this.toJson, this.unknownEnumValue, + this.extra, }); /// Sentinel value for use with [unknownEnumValue]. diff --git a/json_serializable/lib/src/decode_helper.dart b/json_serializable/lib/src/decode_helper.dart index 341446b92..743c83b98 100644 --- a/json_serializable/lib/src/decode_helper.dart +++ b/json_serializable/lib/src/decode_helper.dart @@ -25,6 +25,7 @@ abstract class DecodeHelper implements HelperCore { CreateFactoryResult createFactory( Map accessibleFields, Map unavailableReasons, + List extras, ) { assert(config.createFactory); final buffer = StringBuffer(); @@ -47,6 +48,16 @@ abstract class DecodeHelper implements HelperCore { } } + if (extras.isNotEmpty) { + buffer.writeln(', {'); + for (final extra in extras) { + if (!extra.type.isNullableType) buffer.write('required '); + buffer.writeln( + '${extra.type.getDisplayString(withNullability: true)} ${extra.name},'); + } + buffer.write('}'); + } + buffer.write(')'); final fromJsonLines = []; @@ -56,15 +67,17 @@ abstract class DecodeHelper implements HelperCore { _deserializeForField(accessibleFields[paramOrFieldName]!, ctorParam: ctorParam); + final extraNames = [for (final extra in extras) extra.name]; final data = _writeConstructorInvocation( element, config.constructor, - accessibleFields.keys, + [...accessibleFields.keys, ...extraNames], accessibleFields.values .where((fe) => element.lookUpSetter(fe.name, element.library) != null) .map((fe) => fe.name) .toList(), unavailableReasons, + extraNames, deserializeFun, ); @@ -265,6 +278,7 @@ _ConstructorData _writeConstructorInvocation( Iterable availableConstructorParameters, Iterable writableFields, Map unavailableReasons, + List extras, String Function(String paramOrFieldName, {ParameterElement ctorParam}) deserializeForField, ) { @@ -301,7 +315,7 @@ _ConstructorData _writeConstructorInvocation( } else { constructorArguments.add(arg); } - usedCtorParamsAndFields.add(arg.name); + if (!extras.contains(arg.name)) usedCtorParamsAndFields.add(arg.name); } // fields that aren't already set by the constructor and that aren't final @@ -320,8 +334,9 @@ _ConstructorData _writeConstructorInvocation( buffer ..writeln() ..writeAll(constructorArguments.map((paramElement) { - final content = - deserializeForField(paramElement.name, ctorParam: paramElement); + final content = extras.contains(paramElement.name) + ? paramElement.name + : deserializeForField(paramElement.name, ctorParam: paramElement); return ' $content,\n'; })); } @@ -329,8 +344,9 @@ _ConstructorData _writeConstructorInvocation( buffer ..writeln() ..writeAll(namedConstructorArguments.map((paramElement) { - final value = - deserializeForField(paramElement.name, ctorParam: paramElement); + final value = extras.contains(paramElement.name) + ? paramElement.name + : deserializeForField(paramElement.name, ctorParam: paramElement); return ' ${paramElement.name}: $value,\n'; })); } diff --git a/json_serializable/lib/src/generator_helper.dart b/json_serializable/lib/src/generator_helper.dart index f4016292a..aaebe4cea 100644 --- a/json_serializable/lib/src/generator_helper.dart +++ b/json_serializable/lib/src/generator_helper.dart @@ -58,6 +58,8 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { // these fields. final unavailableReasons = {}; + final extras = []; + final accessibleFields = sortedFields.fold>( {}, (map, field) { @@ -68,21 +70,26 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { unavailableReasons[field.name] = 'Setter-only properties are not supported.'; log.warning('Setters are ignored: ${element.name}.${field.name}'); - } else if (jsonKeyFor(field).ignore) { - unavailableReasons[field.name] = - 'It is assigned to an ignored field.'; } else { - assert(!map.containsKey(field.name)); - map[field.name] = field; + final jsonKey = jsonKeyFor(field); + if (jsonKey.ignore) { + unavailableReasons[field.name] = + 'It is assigned to an ignored field.'; + } else if (jsonKey.extra) { + extras.add(field); + } else { + assert(!map.containsKey(field.name)); + map[field.name] = field; + } } - return map; }, ); var accessibleFieldSet = accessibleFields.values.toSet(); if (config.createFactory) { - final createResult = createFactory(accessibleFields, unavailableReasons); + final createResult = + createFactory(accessibleFields, unavailableReasons, extras); yield createResult.output; accessibleFieldSet = accessibleFields.entries diff --git a/json_serializable/lib/src/json_key_utils.dart b/json_serializable/lib/src/json_key_utils.dart index 3d2543163..0317d0865 100644 --- a/json_serializable/lib/src/json_key_utils.dart +++ b/json_serializable/lib/src/json_key_utils.dart @@ -227,6 +227,7 @@ KeyConfig _from(FieldElement element, ClassConfig classAnnotation) { readValueFunctionName: readValueFunctionName, required: obj.read('required').literalValue as bool?, unknownEnumValue: _annotationValue('unknownEnumValue', mustBeEnum: true), + extra: obj.read('extra').literalValue as bool?, ); } @@ -241,6 +242,7 @@ KeyConfig _populateJsonKey( String? readValueFunctionName, bool? required, String? unknownEnumValue, + bool? extra, }) { if (disallowNullValue == true) { if (includeIfNull == true) { @@ -261,6 +263,7 @@ KeyConfig _populateJsonKey( readValueFunctionName: readValueFunctionName, required: required ?? false, unknownEnumValue: unknownEnumValue, + extra: extra ?? false, ); } diff --git a/json_serializable/lib/src/type_helpers/config_types.dart b/json_serializable/lib/src/type_helpers/config_types.dart index d352d16af..4cad826f4 100644 --- a/json_serializable/lib/src/type_helpers/config_types.dart +++ b/json_serializable/lib/src/type_helpers/config_types.dart @@ -23,6 +23,8 @@ class KeyConfig { final String? readValueFunctionName; + final bool extra; + KeyConfig({ required this.defaultValue, required this.disallowNullValue, @@ -32,6 +34,7 @@ class KeyConfig { required this.readValueFunctionName, required this.required, required this.unknownEnumValue, + required this.extra, }); }