From e303f172fc28bf4a95bd93e99b9b701522161f08 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Sun, 30 Mar 2025 12:47:51 +0300 Subject: [PATCH 01/18] feat: implement first version of sealed class serialization --- example/lib/sealed_class_example.dart | 38 ++ example/lib/sealed_class_example.g.dart | 53 +++ .../lib/src/allowed_keys_helpers.dart | 18 + .../lib/src/json_serializable.dart | 40 +++ .../lib/src/json_serializable.g.dart | 22 ++ json_serializable/lib/src/decode_helper.dart | 324 +++++++++++++----- json_serializable/lib/src/encoder_helper.dart | 110 +++++- json_serializable/lib/src/helper_core.dart | 2 +- .../lib/src/type_helpers/config_types.dart | 12 + json_serializable/lib/src/utils.dart | 30 ++ 10 files changed, 544 insertions(+), 105 deletions(-) create mode 100644 example/lib/sealed_class_example.dart create mode 100644 example/lib/sealed_class_example.g.dart diff --git a/example/lib/sealed_class_example.dart b/example/lib/sealed_class_example.dart new file mode 100644 index 00000000..6e56cda0 --- /dev/null +++ b/example/lib/sealed_class_example.dart @@ -0,0 +1,38 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'sealed_class_example.g.dart'; + +@JsonSerializable( + unionDiscriminator: 'runtimeType', + unionRename: UnionRename.snake, +) +sealed class MySealedClass { + MySealedClass(this.value); + + factory MySealedClass.fromJson(Map json) => + _$MySealedClassFromJson(json); + + String value; + + Map toJson() => _$MySealedClassToJson(this); +} + +@JsonSerializable() +class FirstSubtype extends MySealedClass { + final String someAttribute; + + FirstSubtype( + this.someAttribute, + super.value, + ); +} + +@JsonSerializable() +class SecondSubtype extends MySealedClass { + final String someOtherAttribute; + + SecondSubtype( + this.someOtherAttribute, + super.value, + ); +} diff --git a/example/lib/sealed_class_example.g.dart b/example/lib/sealed_class_example.g.dart new file mode 100644 index 00000000..af7adf3f --- /dev/null +++ b/example/lib/sealed_class_example.g.dart @@ -0,0 +1,53 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'sealed_class_example.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MySealedClass _$MySealedClassFromJson(Map json) => + switch (json['runtimeType']) { + 'first_subtype' => _$FirstSubtypeFromJson(json), + 'second_subtype' => _$SecondSubtypeFromJson(json), + _ => throw UnrerecognizedUnionTypeException( + '${json['runtimeType']}', + MySealedClass, + json, + ), + }; + +Map _$MySealedClassToJson(MySealedClass instance) => + switch (instance) { + final FirstSubtype instance => { + 'runtimeType': 'first_subtype', + ..._$FirstSubtypeToJson(instance), + }, + final SecondSubtype instance => { + 'runtimeType': 'second_subtype', + ..._$SecondSubtypeToJson(instance), + }, + }; + +FirstSubtype _$FirstSubtypeFromJson(Map json) => FirstSubtype( + json['someAttribute'] as String, + json['value'] as String, + ); + +Map _$FirstSubtypeToJson(FirstSubtype instance) => + { + 'value': instance.value, + 'someAttribute': instance.someAttribute, + }; + +SecondSubtype _$SecondSubtypeFromJson(Map json) => + SecondSubtype( + json['someOtherAttribute'] as String, + json['value'] as String, + ); + +Map _$SecondSubtypeToJson(SecondSubtype instance) => + { + 'value': instance.value, + 'someOtherAttribute': instance.someOtherAttribute, + }; diff --git a/json_annotation/lib/src/allowed_keys_helpers.dart b/json_annotation/lib/src/allowed_keys_helpers.dart index 5ea896a7..087982c9 100644 --- a/json_annotation/lib/src/allowed_keys_helpers.dart +++ b/json_annotation/lib/src/allowed_keys_helpers.dart @@ -79,6 +79,24 @@ class UnrecognizedKeysException extends BadKeyException { : super._(map); } +/// Exception thrown if there is an unrecognized union type in a JSON map +/// that was provided during deserialization. +class UnrerecognizedUnionTypeException extends BadKeyException { + /// The discriminator that was not recognized. + final String unrecognizedType; + + /// The type of the union that was being deserialized. + final Type unionType; + + @override + String get message => 'Unrecognized type: $unrecognizedType ' + 'for union: $unionType.'; + + UnrerecognizedUnionTypeException( + this.unrecognizedType, this.unionType, Map map) + : super._(map); +} + /// Exception thrown if there are missing required keys in a JSON map that was /// provided during deserialization. class MissingRequiredKeysException extends BadKeyException { diff --git a/json_annotation/lib/src/json_serializable.dart b/json_annotation/lib/src/json_serializable.dart index 7c82e2f2..dac18f53 100644 --- a/json_annotation/lib/src/json_serializable.dart +++ b/json_annotation/lib/src/json_serializable.dart @@ -31,6 +31,26 @@ enum FieldRename { screamingSnake, } +/// Values for the automatic class renaming behavior for [JsonSerializable] +/// with sealed classes. +enum UnionRename { + /// Use the union class name without changes. + none, + + /// Encodes union class named `KebabCase` with a JSON key `kebab-case`. + kebab, + + /// Encodes union class named `SnakeCase` with a JSON key `snake_case`. + snake, + + /// Encodes union class named `PascalCase` with a JSON key `PascalCase`. + pascal, + + /// Encodes union class named `ScreamingSnakeCase` with a JSON key + /// `SCREAMING_SNAKE_CASE` + screamingSnake, +} + /// An annotation used to specify a class to generate code for. @JsonSerializable( checked: true, @@ -162,6 +182,20 @@ class JsonSerializable { /// fields annotated with [JsonKey]. final FieldRename? fieldRename; + /// Defines the automatic naming strategy when converting class names + /// to union type names. + /// + /// With a value [UnionRename.none] (the default), the name of the class is + /// used without modification. + /// + /// See [UnionRename] for details on the other options. + final UnionRename? unionRename; + + /// The discriminator key used to identify the union type. + /// + /// Defaults to `type`. + final String? unionDiscriminator; + /// When `true` on classes with type parameters (generic types), extra /// "helper" parameters will be generated for `fromJson` and/or `toJson` to /// support serializing values of those types. @@ -271,6 +305,8 @@ class JsonSerializable { this.disallowUnrecognizedKeys, this.explicitToJson, this.fieldRename, + this.unionRename, + this.unionDiscriminator, this.ignoreUnannotated, this.includeIfNull, this.converters, @@ -293,6 +329,8 @@ class JsonSerializable { disallowUnrecognizedKeys: false, explicitToJson: false, fieldRename: FieldRename.none, + unionRename: UnionRename.none, + unionDiscriminator: 'type', ignoreUnannotated: false, includeIfNull: true, genericArgumentFactories: false, @@ -314,6 +352,8 @@ class JsonSerializable { disallowUnrecognizedKeys ?? defaults.disallowUnrecognizedKeys, explicitToJson: explicitToJson ?? defaults.explicitToJson, fieldRename: fieldRename ?? defaults.fieldRename, + unionRename: unionRename ?? defaults.unionRename, + unionDiscriminator: unionDiscriminator ?? defaults.unionDiscriminator, ignoreUnannotated: ignoreUnannotated ?? defaults.ignoreUnannotated, includeIfNull: includeIfNull ?? defaults.includeIfNull, genericArgumentFactories: diff --git a/json_annotation/lib/src/json_serializable.g.dart b/json_annotation/lib/src/json_serializable.g.dart index 80a3e4c8..1772c254 100644 --- a/json_annotation/lib/src/json_serializable.g.dart +++ b/json_annotation/lib/src/json_serializable.g.dart @@ -26,6 +26,8 @@ JsonSerializable _$JsonSerializableFromJson( 'disallow_unrecognized_keys', 'explicit_to_json', 'field_rename', + 'union_rename', + 'union_discriminator', 'generic_argument_factories', 'ignore_unannotated', 'include_if_null', @@ -48,6 +50,14 @@ JsonSerializable _$JsonSerializableFromJson( 'field_rename', (v) => $enumDecodeNullable(_$FieldRenameEnumMap, v), ), + unionRename: $checkedConvert( + 'union_rename', + (v) => $enumDecodeNullable(_$UnionRenameEnumMap, v), + ), + unionDiscriminator: $checkedConvert( + 'union_discriminator', + (v) => v as String?, + ), ignoreUnannotated: $checkedConvert( 'ignore_unannotated', (v) => v as bool?, @@ -73,6 +83,8 @@ JsonSerializable _$JsonSerializableFromJson( 'disallowUnrecognizedKeys': 'disallow_unrecognized_keys', 'explicitToJson': 'explicit_to_json', 'fieldRename': 'field_rename', + 'unionRename': 'union_rename', + 'unionDiscriminator': 'union_discriminator', 'ignoreUnannotated': 'ignore_unannotated', 'includeIfNull': 'include_if_null', 'genericArgumentFactories': 'generic_argument_factories', @@ -93,6 +105,8 @@ Map _$JsonSerializableToJson(JsonSerializable instance) => 'disallow_unrecognized_keys': instance.disallowUnrecognizedKeys, 'explicit_to_json': instance.explicitToJson, 'field_rename': _$FieldRenameEnumMap[instance.fieldRename], + 'union_rename': _$UnionRenameEnumMap[instance.unionRename], + 'union_discriminator': instance.unionDiscriminator, 'generic_argument_factories': instance.genericArgumentFactories, 'ignore_unannotated': instance.ignoreUnannotated, 'include_if_null': instance.includeIfNull, @@ -105,3 +119,11 @@ const _$FieldRenameEnumMap = { FieldRename.pascal: 'pascal', FieldRename.screamingSnake: 'screamingSnake', }; + +const _$UnionRenameEnumMap = { + UnionRename.none: 'none', + UnionRename.kebab: 'kebab', + UnionRename.snake: 'snake', + UnionRename.pascal: 'pascal', + UnionRename.screamingSnake: 'screamingSnake', +}; diff --git a/json_serializable/lib/src/decode_helper.dart b/json_serializable/lib/src/decode_helper.dart index 633935e7..ae44781e 100644 --- a/json_serializable/lib/src/decode_helper.dart +++ b/json_serializable/lib/src/decode_helper.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:core'; + import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:build/build.dart'; @@ -27,31 +29,6 @@ mixin DecodeHelper implements HelperCore { Map unavailableReasons, ) { assert(config.createFactory); - final buffer = StringBuffer(); - - final mapType = config.anyMap ? 'Map' : 'Map'; - buffer.write( - '$targetClassReference ' - '${prefix}FromJson${genericClassArgumentsImpl(withConstraints: true)}' - '($mapType json', - ); - - if (config.genericArgumentFactories) { - for (var arg in element.typeParameters) { - final helperName = fromJsonForType( - arg.instantiate(nullabilitySuffix: NullabilitySuffix.none), - ); - - buffer.write(', ${arg.name} Function(Object? json) $helperName'); - } - if (element.typeParameters.isNotEmpty) { - buffer.write(','); - } - } - - buffer.write(')'); - - final fromJsonLines = []; String deserializeFun( String paramOrFieldName, { @@ -79,88 +56,259 @@ mixin DecodeHelper implements HelperCore { ), ).toList(); - if (config.checked) { - final classLiteral = escapeDartString(element.name); + final functionBodyParts = switch (( + isSealed: element.isSealed, + isChecked: config.checked, + )) { + (isSealed: true, isChecked: _) => [_createSealedFunctionExpressionBody()], + (isSealed: _, isChecked: true) => [ + _createCheckedFunctionExpressionBody(data, checks, accessibleFields), + ], + _ => _createDefaultFunctionBody(data, checks, deserializeFun), + }; + + return CreateFactoryResult( + _createFromJsonFunctionSignature(functionBodyParts), + data.usedCtorParamsAndFields, + ); + } - final sectionBuffer = StringBuffer() - ..write(''' + /// Creates the function signature around [functionBodyParts] + /// that will be used to deserialize the class. + /// + /// If [functionBodyParts] has only one element, expression body is used. + /// + /// ```dart + /// ''' + /// ExampleClass _$ExampleClassFromJson(Map json) => + /// /* only body part here */ + /// ''' + /// ``` + /// + /// If [functionBodyParts] has more than one element, block body is used. + /// + /// ```dart + /// ''' + /// ExampleClass _$ExampleClassFromJson(Map json) { + /// /* first body parts here */ + /// + /// return /* last body part here */; + /// } + /// ''' + /// ``` + String _createFromJsonFunctionSignature(Iterable functionBodyParts) { + final mapType = config.anyMap ? 'Map' : 'Map'; + + final buffer = StringBuffer() + ..write( + '$targetClassReference ' + '${prefix}FromJson${genericClassArgumentsImpl(withConstraints: true)}' + '($mapType json', + ); + + if (config.genericArgumentFactories) { + for (var arg in element.typeParameters) { + final helperName = fromJsonForType( + arg.instantiate(nullabilitySuffix: NullabilitySuffix.none), + ); + + buffer.write(', ${arg.name} Function(Object? json) $helperName'); + } + if (element.typeParameters.isNotEmpty) { + buffer.write(','); + } + } + + buffer.write(')'); + + if (functionBodyParts case [final single]) { + buffer.write('=> $single'); + } else { + buffer + ..writeln('{') + ..writeAll(functionBodyParts.take(functionBodyParts.length - 1)) + ..writeln('return ${functionBodyParts.last}') + ..writeln('}'); + } + + return buffer.toString(); + } + + /// Creates the body of the function that deserializes a sealed class. + /// + /// For example: + /// ```dart + /// ''' + /// switch (json['type']) { + /// 'FirstSubtype' => _$FirstSubtypeFromJson(json), + /// 'SecondSubtype' => _$SecondSubtypeFromJson(json), + /// _ => throw Exception('Unknown type: ${json['type']}'), + /// }; + /// ''' + /// ``` + String _createSealedFunctionExpressionBody() { + final implementations = sealedClassImplementations(element); + final discriminator = config.unionDiscriminator; + + String buildSingleImpl(ClassElement impl) { + final unionName = encodedUnionName(config.unionRename, impl.name); + + return "'$unionName' => ${classPrefix(impl)}FromJson(json),"; + } + + final sectionBuffer = StringBuffer() + ..write("switch (json['$discriminator']) {") + ..writeAll(implementations.map(buildSingleImpl), '\n') + ..writeln(''' +_ => throw UnrerecognizedUnionTypeException( + '\${json['$discriminator']}', + ${element.name}, + json, +),''') + ..writeln('};'); + + return sectionBuffer.toString(); + } + + /// Creates the body of the function that deserializes a class with checked + /// mode. + /// + /// For example: + /// ```dart + /// ''' + /// $checkedCreate( + /// 'FirstSubtype', + /// json, + /// ($checkedConvert) { + /// $checkKeys( + /// json, + /// allowedKeys: const ['value', 'someAttribute'], + /// ); + /// final val = FirstSubtype( + /// $checkedConvert('someAttribute', (v) => v as String), + /// $checkedConvert('value', (v) => v as String), + /// ); + /// return val; + /// }, + /// ); + /// ''' + /// ``` + String _createCheckedFunctionExpressionBody( + _ConstructorData data, + List checks, + Map accessibleFields, + ) { + final classLiteral = escapeDartString(element.name); + + final sectionBuffer = StringBuffer() + ..write(''' \$checkedCreate( $classLiteral, json, (\$checkedConvert) {\n''') - ..write(checks.join()) - ..write(''' + ..write(checks.join()) + ..write(''' final val = ${data.content};'''); - for (final fieldName in data.fieldsToSet) { - sectionBuffer.writeln(); - final fieldValue = accessibleFields[fieldName]!; - final safeName = safeNameAccess(fieldValue); - sectionBuffer - ..write(''' + for (final fieldName in data.fieldsToSet) { + sectionBuffer.writeln(); + final fieldValue = accessibleFields[fieldName]!; + final safeName = safeNameAccess(fieldValue); + sectionBuffer + ..write(''' \$checkedConvert($safeName, (v) => ''') - ..write('val.$fieldName = ') - ..write(_deserializeForField(fieldValue, checkedProperty: true)); - - final readValueFunc = jsonKeyFor(fieldValue).readValueFunctionName; - if (readValueFunc != null) { - sectionBuffer.writeln(',readValue: $readValueFunc,'); - } + ..write('val.$fieldName = ') + ..write(_deserializeForField(fieldValue, checkedProperty: true)); - sectionBuffer.write(');'); + final readValueFunc = jsonKeyFor(fieldValue).readValueFunctionName; + if (readValueFunc != null) { + sectionBuffer.writeln(',readValue: $readValueFunc,'); } - sectionBuffer.write('''\n return val; - }'''); + sectionBuffer.write(');'); + } - final fieldKeyMap = Map.fromEntries( - data.usedCtorParamsAndFields - .map((k) => MapEntry(k, nameAccess(accessibleFields[k]!))) - .where((me) => me.key != me.value), - ); + sectionBuffer.write('''\n return val; + }'''); - String fieldKeyMapArg; - if (fieldKeyMap.isEmpty) { - fieldKeyMapArg = ''; - } else { - final mapLiteral = jsonMapAsDart(fieldKeyMap); - fieldKeyMapArg = ', fieldKeyMap: const $mapLiteral'; - } + final fieldKeyMap = Map.fromEntries( + data.usedCtorParamsAndFields + .map((k) => MapEntry(k, nameAccess(accessibleFields[k]!))) + .where((me) => me.key != me.value), + ); - sectionBuffer - ..write(fieldKeyMapArg) - ..write(',);'); - fromJsonLines.add(sectionBuffer.toString()); + String fieldKeyMapArg; + if (fieldKeyMap.isEmpty) { + fieldKeyMapArg = ''; } else { - fromJsonLines.addAll(checks); - - final sectionBuffer = StringBuffer() - ..write(''' - ${data.content}'''); - for (final field in data.fieldsToSet) { - sectionBuffer - ..writeln() - ..write(' ..$field = ') - ..write(deserializeFun(field)); - } - sectionBuffer.writeln(';'); - fromJsonLines.add(sectionBuffer.toString()); + final mapLiteral = jsonMapAsDart(fieldKeyMap); + fieldKeyMapArg = ', fieldKeyMap: const $mapLiteral'; } - if (fromJsonLines.length == 1) { - buffer - ..write('=>') - ..write(fromJsonLines.single); - } else { - buffer - ..write('{') - ..writeAll(fromJsonLines.take(fromJsonLines.length - 1)) - ..write('return ') - ..write(fromJsonLines.last) - ..write('}'); + sectionBuffer + ..write(fieldKeyMapArg) + ..write(',);'); + + return sectionBuffer.toString(); + } + + /// Creates the body of the function that deserializes a class. + /// + /// If there are no checks will return a single constructor invocation. + /// + /// ```dart + /// [ + /// ''' + /// ExampleClass( + /// json['exampleField'] as String, + /// ) + /// ''' + /// ] + /// /* OR with fields to set */ + /// [ + /// ''' + /// ExampleClass( + /// json['exampleField'] as String, + /// ) + /// ..field1 = json['field1'] as String + /// ..field2 = json['field2'] as String; + /// ''' + /// ] + /// ``` + /// + /// If there are checks, will return the checks followed by the + /// constructor invocation. + /// ```dart + /// [ + /// ''' + /// $checkKeys( + /// json, + /// allowedKeys: const ['exampleField', 'field1', 'field2'], + /// ) + /// ''', + /// ''' + /// ExampleClass( + /// json['exampleField'] as String, + /// ) + /// ''' + /// ] + /// ``` + /// + List _createDefaultFunctionBody( + _ConstructorData data, + List checks, + String Function(String paramOrFieldName, {ParameterElement ctorParam}) + deserializeForField, + ) { + final sectionBuffer = StringBuffer() + ..write(''' + ${data.content}'''); + for (final field in data.fieldsToSet) { + sectionBuffer.writeln('..$field = ${deserializeForField(field)}'); } + sectionBuffer.writeln(';'); - return CreateFactoryResult(buffer.toString(), data.usedCtorParamsAndFields); + return [...checks, sectionBuffer.toString()]; } Iterable _checkKeys(Iterable accessibleFields) sync* { diff --git a/json_serializable/lib/src/encoder_helper.dart b/json_serializable/lib/src/encoder_helper.dart index 7effcd05..0f6f2cf5 100644 --- a/json_serializable/lib/src/encoder_helper.dart +++ b/json_serializable/lib/src/encoder_helper.dart @@ -11,6 +11,7 @@ import 'helper_core.dart'; import 'type_helpers/generic_factory_helper.dart'; import 'type_helpers/json_converter_helper.dart'; import 'unsupported_type_error.dart'; +import 'utils.dart'; mixin EncodeHelper implements HelperCore { String _fieldAccess(FieldElement field) => '$_toJsonParamName.${field.name}'; @@ -70,7 +71,6 @@ mixin EncodeHelper implements HelperCore { final buffer = StringBuffer( 'abstract final class _\$${element.name.nonPrivate}JsonKeys {', ); - // ..write('static const _\$${element.name.nonPrivate}JsonKeys();'); for (final field in accessibleFieldSet) { buffer.writeln( @@ -87,6 +87,25 @@ mixin EncodeHelper implements HelperCore { Iterable createToJson(Set accessibleFields) sync* { assert(config.createToJson); + final expressionBody = element.isSealed + ? _createSealedFunctionExpressionBody() + : _createFieldMapFunctionExpressionBody(accessibleFields); + + yield _createToJsonFunctionSignature(expressionBody); + } + + /// Creates the function signature around [functionExpressionBody] + /// that will be used to serialize the class. + /// + /// For example: + /// + /// ```dart + /// ''' + /// Map _$ExampleClassToJson(ExampleClass instance) => + /// /* expression body here */; + /// ''' + /// ``` + String _createToJsonFunctionSignature(String functionExpressionBody) { final buffer = StringBuffer(); final functionName = @@ -98,25 +117,84 @@ mixin EncodeHelper implements HelperCore { if (config.genericArgumentFactories) _writeGenericArgumentFactories(buffer); - buffer - ..write(') ') - ..writeln('=> {') - ..writeAll( - accessibleFields.map((field) { - final access = _fieldAccess(field); + buffer.write(') => $functionExpressionBody;'); - final keyExpression = safeNameAccess(field); - final valueExpression = _serializeField(field, access); + return buffer.toString(); + } - final maybeQuestion = _canWriteJsonWithoutNullCheck(field) ? '' : '?'; + /// Creates expression body for a function that serializes a union class. + /// + /// For example: + /// ```dart + /// ''' + /// switch (instance) { + /// final FirstSubtype instance => { + /// 'type': 'FirstSubtype', + /// ..._$FirstSubtypeToJson(instance), + /// }, + /// final SecondSubtype instance => { + /// 'type': 'SecondSubtype', + /// ..._$SecondSubtypeToJson(instance), + /// }, + /// } + /// ''' + /// ``` + String _createSealedFunctionExpressionBody() { + final implementations = sealedClassImplementations(element); + + final discriminator = config.unionDiscriminator; + + String buildSingleImpl(ClassElement impl) { + final originalName = impl.name; + + final unionName = encodedUnionName(config.unionRename, originalName); + + return ''' + final $originalName instance => { + '$discriminator': '$unionName', + ...${classPrefix(impl)}ToJson(instance), + }, +'''; + } - final keyValuePair = '$keyExpression: $maybeQuestion$valueExpression'; - return ' $keyValuePair,\n'; - }), - ) - ..writeln('};'); + final buffer = StringBuffer() + ..writeln('switch (instance) {') + ..writeAll(implementations.map(buildSingleImpl)) + ..writeln('}'); - yield buffer.toString(); + return buffer.toString(); + } + + /// Creates expression body for a function that serializes a class. + /// + /// For example: + /// ```dart + /// ''' + /// { + /// 'exampleField': instance.exampleField, + /// } + /// ''' + /// ``` + String _createFieldMapFunctionExpressionBody( + Set accessibleFields, + ) { + String buildSingleField(FieldElement field) { + final access = _fieldAccess(field); + + final keyExpression = safeNameAccess(field); + final valueExpression = _serializeField(field, access); + + final maybeQuestion = _canWriteJsonWithoutNullCheck(field) ? '' : '?'; + + return '$keyExpression: $maybeQuestion$valueExpression,'; + } + + final buffer = StringBuffer() + ..writeln('{') + ..writeAll(accessibleFields.map(buildSingleField)) + ..writeln('}'); + + return buffer.toString(); } void _writeGenericArgumentFactories(StringBuffer buffer) { diff --git a/json_serializable/lib/src/helper_core.dart b/json_serializable/lib/src/helper_core.dart index 6e9e731a..0abc892a 100644 --- a/json_serializable/lib/src/helper_core.dart +++ b/json_serializable/lib/src/helper_core.dart @@ -38,7 +38,7 @@ abstract class HelperCore { escapeDartString(nameAccess(field)); @protected - String get prefix => '_\$${element.name.nonPrivate}'; + String get prefix => classPrefix(element); /// Returns a [String] representing the type arguments that exist on /// [element]. diff --git a/json_serializable/lib/src/type_helpers/config_types.dart b/json_serializable/lib/src/type_helpers/config_types.dart index 80d25858..6ebf52a8 100644 --- a/json_serializable/lib/src/type_helpers/config_types.dart +++ b/json_serializable/lib/src/type_helpers/config_types.dart @@ -54,6 +54,8 @@ class ClassConfig { final bool disallowUnrecognizedKeys; final bool explicitToJson; final FieldRename fieldRename; + final UnionRename unionRename; + final String unionDiscriminator; final bool genericArgumentFactories; final bool ignoreUnannotated; final bool includeIfNull; @@ -72,6 +74,8 @@ class ClassConfig { required this.disallowUnrecognizedKeys, required this.explicitToJson, required this.fieldRename, + required this.unionRename, + required this.unionDiscriminator, required this.genericArgumentFactories, required this.ignoreUnannotated, required this.includeIfNull, @@ -105,6 +109,10 @@ class ClassConfig { config.genericArgumentFactories ?? ClassConfig.defaults.genericArgumentFactories, fieldRename: config.fieldRename ?? ClassConfig.defaults.fieldRename, + unionRename: config.unionRename ?? ClassConfig.defaults.unionRename, + unionDiscriminator: + config.unionDiscriminator ?? + ClassConfig.defaults.unionDiscriminator, disallowUnrecognizedKeys: config.disallowUnrecognizedKeys ?? ClassConfig.defaults.disallowUnrecognizedKeys, @@ -125,6 +133,8 @@ class ClassConfig { disallowUnrecognizedKeys: false, explicitToJson: false, fieldRename: FieldRename.none, + unionRename: UnionRename.none, + unionDiscriminator: 'type', genericArgumentFactories: false, ignoreUnannotated: false, includeIfNull: true, @@ -144,6 +154,8 @@ class ClassConfig { includeIfNull: includeIfNull, genericArgumentFactories: genericArgumentFactories, fieldRename: fieldRename, + unionRename: unionRename, + unionDiscriminator: unionDiscriminator, disallowUnrecognizedKeys: disallowUnrecognizedKeys, // TODO typeConverters = [] ); diff --git a/json_serializable/lib/src/utils.dart b/json_serializable/lib/src/utils.dart index 9f1d34bc..58ac8fd4 100644 --- a/json_serializable/lib/src/utils.dart +++ b/json_serializable/lib/src/utils.dart @@ -61,6 +61,8 @@ JsonSerializable _valueForAnnotation(ConstantReader reader) => JsonSerializable( reader.read('disallowUnrecognizedKeys').literalValue as bool?, explicitToJson: reader.read('explicitToJson').literalValue as bool?, fieldRename: readEnum(reader.read('fieldRename'), FieldRename.values), + unionRename: readEnum(reader.read('unionRename'), UnionRename.values), + unionDiscriminator: reader.read('unionDiscriminator').literalValue as String?, genericArgumentFactories: reader.read('genericArgumentFactories').literalValue as bool?, ignoreUnannotated: reader.read('ignoreUnannotated').literalValue as bool?, @@ -114,6 +116,9 @@ ClassConfig mergeConfig( annotation.disallowUnrecognizedKeys ?? config.disallowUnrecognizedKeys, explicitToJson: annotation.explicitToJson ?? config.explicitToJson, fieldRename: annotation.fieldRename ?? config.fieldRename, + unionRename: annotation.unionRename ?? config.unionRename, + unionDiscriminator: + annotation.unionDiscriminator ?? config.unionDiscriminator, genericArgumentFactories: annotation.genericArgumentFactories ?? (classElement.typeParameters.isNotEmpty && @@ -162,6 +167,20 @@ ConstructorElement constructorByName(ClassElement classElement, String name) { return ctor; } +Iterable sealedClassImplementations( + ClassElement maybeSealedClass, +) { + if (maybeSealedClass case final sc when sc.isSealed) { + return LibraryReader(sc.library) + .annotatedWith(const TypeChecker.fromRuntime(JsonSerializable)) + .map((e) => e.element) + .whereType() + .where((e) => e.allSupertypes.contains(sc.thisType)); + } + + return []; +} + /// If [targetType] is an enum, returns the [FieldElement] instances associated /// with its values. /// @@ -196,6 +215,17 @@ String encodedFieldName(FieldRename fieldRename, String declaredName) => FieldRename.pascal => declaredName.pascal, }; +String encodedUnionName(UnionRename unionRename, String declaredName) => + switch (unionRename) { + UnionRename.none => declaredName, + UnionRename.snake => declaredName.snake, + UnionRename.screamingSnake => declaredName.snake.toUpperCase(), + UnionRename.kebab => declaredName.kebab, + UnionRename.pascal => declaredName.pascal, + }; + +String classPrefix(ClassElement element) => '_\$${element.name.nonPrivate}'; + /// Return the Dart code presentation for the given [type]. /// /// This function is intentionally limited, and does not support all possible From 1f12cd4158364abd14779b122a054d1cd7aaa14a Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Sun, 30 Mar 2025 13:17:35 +0300 Subject: [PATCH 02/18] fix: add assertions for sealed classes --- json_serializable/lib/src/decode_helper.dart | 3 +++ json_serializable/lib/src/encoder_helper.dart | 2 ++ 2 files changed, 5 insertions(+) diff --git a/json_serializable/lib/src/decode_helper.dart b/json_serializable/lib/src/decode_helper.dart index ae44781e..c8ad9a83 100644 --- a/json_serializable/lib/src/decode_helper.dart +++ b/json_serializable/lib/src/decode_helper.dart @@ -147,7 +147,10 @@ mixin DecodeHelper implements HelperCore { /// ''' /// ``` String _createSealedFunctionExpressionBody() { + assert(element.isSealed); + final implementations = sealedClassImplementations(element); + final discriminator = config.unionDiscriminator; String buildSingleImpl(ClassElement impl) { diff --git a/json_serializable/lib/src/encoder_helper.dart b/json_serializable/lib/src/encoder_helper.dart index 0f6f2cf5..98fd7156 100644 --- a/json_serializable/lib/src/encoder_helper.dart +++ b/json_serializable/lib/src/encoder_helper.dart @@ -140,6 +140,8 @@ mixin EncodeHelper implements HelperCore { /// ''' /// ``` String _createSealedFunctionExpressionBody() { + assert(element.isSealed); + final implementations = sealedClassImplementations(element); final discriminator = config.unionDiscriminator; From 6bf34e899d74d180463ce14c52a4c2415358b4f2 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 2 Apr 2025 15:48:29 -0700 Subject: [PATCH 03/18] Fix exception name --- example/lib/sealed_class_example.g.dart | 2 +- json_annotation/lib/src/allowed_keys_helpers.dart | 5 ++--- json_serializable/lib/src/decode_helper.dart | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/example/lib/sealed_class_example.g.dart b/example/lib/sealed_class_example.g.dart index af7adf3f..a50f0971 100644 --- a/example/lib/sealed_class_example.g.dart +++ b/example/lib/sealed_class_example.g.dart @@ -10,7 +10,7 @@ MySealedClass _$MySealedClassFromJson(Map json) => switch (json['runtimeType']) { 'first_subtype' => _$FirstSubtypeFromJson(json), 'second_subtype' => _$SecondSubtypeFromJson(json), - _ => throw UnrerecognizedUnionTypeException( + _ => throw UnrecognizedUnionTypeException( '${json['runtimeType']}', MySealedClass, json, diff --git a/json_annotation/lib/src/allowed_keys_helpers.dart b/json_annotation/lib/src/allowed_keys_helpers.dart index 087982c9..b0e87cc8 100644 --- a/json_annotation/lib/src/allowed_keys_helpers.dart +++ b/json_annotation/lib/src/allowed_keys_helpers.dart @@ -81,7 +81,7 @@ class UnrecognizedKeysException extends BadKeyException { /// Exception thrown if there is an unrecognized union type in a JSON map /// that was provided during deserialization. -class UnrerecognizedUnionTypeException extends BadKeyException { +class UnrecognizedUnionTypeException extends BadKeyException { /// The discriminator that was not recognized. final String unrecognizedType; @@ -92,8 +92,7 @@ class UnrerecognizedUnionTypeException extends BadKeyException { String get message => 'Unrecognized type: $unrecognizedType ' 'for union: $unionType.'; - UnrerecognizedUnionTypeException( - this.unrecognizedType, this.unionType, Map map) + UnrecognizedUnionTypeException(this.unrecognizedType, this.unionType, Map map) : super._(map); } diff --git a/json_serializable/lib/src/decode_helper.dart b/json_serializable/lib/src/decode_helper.dart index c8ad9a83..1f11b8dd 100644 --- a/json_serializable/lib/src/decode_helper.dart +++ b/json_serializable/lib/src/decode_helper.dart @@ -163,7 +163,7 @@ mixin DecodeHelper implements HelperCore { ..write("switch (json['$discriminator']) {") ..writeAll(implementations.map(buildSingleImpl), '\n') ..writeln(''' -_ => throw UnrerecognizedUnionTypeException( +_ => throw UnrecognizedUnionTypeException( '\${json['$discriminator']}', ${element.name}, json, From 4b9e805dfccfd28143bd22e4aee22b8ba5608925 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Fri, 4 Apr 2025 17:46:22 +0300 Subject: [PATCH 04/18] fix: review fixes --- json_serializable/lib/src/decode_helper.dart | 22 ++----------------- json_serializable/lib/src/encoder_helper.dart | 6 ----- json_serializable/lib/src/utils.dart | 2 +- 3 files changed, 3 insertions(+), 27 deletions(-) diff --git a/json_serializable/lib/src/decode_helper.dart b/json_serializable/lib/src/decode_helper.dart index 1f11b8dd..604015db 100644 --- a/json_serializable/lib/src/decode_helper.dart +++ b/json_serializable/lib/src/decode_helper.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'dart:core'; - import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:build/build.dart'; @@ -79,22 +77,18 @@ mixin DecodeHelper implements HelperCore { /// If [functionBodyParts] has only one element, expression body is used. /// /// ```dart - /// ''' /// ExampleClass _$ExampleClassFromJson(Map json) => /// /* only body part here */ - /// ''' /// ``` /// /// If [functionBodyParts] has more than one element, block body is used. /// /// ```dart - /// ''' /// ExampleClass _$ExampleClassFromJson(Map json) { /// /* first body parts here */ /// /// return /* last body part here */; /// } - /// ''' /// ``` String _createFromJsonFunctionSignature(Iterable functionBodyParts) { final mapType = config.anyMap ? 'Map' : 'Map'; @@ -138,13 +132,11 @@ mixin DecodeHelper implements HelperCore { /// /// For example: /// ```dart - /// ''' /// switch (json['type']) { /// 'FirstSubtype' => _$FirstSubtypeFromJson(json), /// 'SecondSubtype' => _$SecondSubtypeFromJson(json), /// _ => throw Exception('Unknown type: ${json['type']}'), /// }; - /// ''' /// ``` String _createSealedFunctionExpressionBody() { assert(element.isSealed); @@ -178,7 +170,6 @@ _ => throw UnrecognizedUnionTypeException( /// /// For example: /// ```dart - /// ''' /// $checkedCreate( /// 'FirstSubtype', /// json, @@ -194,7 +185,6 @@ _ => throw UnrecognizedUnionTypeException( /// return val; /// }, /// ); - /// ''' /// ``` String _createCheckedFunctionExpressionBody( _ConstructorData data, @@ -261,21 +251,17 @@ _ => throw UnrecognizedUnionTypeException( /// /// ```dart /// [ - /// ''' /// ExampleClass( /// json['exampleField'] as String, /// ) - /// ''' /// ] /// /* OR with fields to set */ /// [ - /// ''' /// ExampleClass( /// json['exampleField'] as String, /// ) /// ..field1 = json['field1'] as String - /// ..field2 = json['field2'] as String; - /// ''' + /// ..field2 = json['field2'] as String /// ] /// ``` /// @@ -283,17 +269,13 @@ _ => throw UnrecognizedUnionTypeException( /// constructor invocation. /// ```dart /// [ - /// ''' /// $checkKeys( /// json, /// allowedKeys: const ['exampleField', 'field1', 'field2'], - /// ) - /// ''', - /// ''' + /// ), /// ExampleClass( /// json['exampleField'] as String, /// ) - /// ''' /// ] /// ``` /// diff --git a/json_serializable/lib/src/encoder_helper.dart b/json_serializable/lib/src/encoder_helper.dart index 98fd7156..36b986fc 100644 --- a/json_serializable/lib/src/encoder_helper.dart +++ b/json_serializable/lib/src/encoder_helper.dart @@ -100,10 +100,8 @@ mixin EncodeHelper implements HelperCore { /// For example: /// /// ```dart - /// ''' /// Map _$ExampleClassToJson(ExampleClass instance) => /// /* expression body here */; - /// ''' /// ``` String _createToJsonFunctionSignature(String functionExpressionBody) { final buffer = StringBuffer(); @@ -126,7 +124,6 @@ mixin EncodeHelper implements HelperCore { /// /// For example: /// ```dart - /// ''' /// switch (instance) { /// final FirstSubtype instance => { /// 'type': 'FirstSubtype', @@ -137,7 +134,6 @@ mixin EncodeHelper implements HelperCore { /// ..._$SecondSubtypeToJson(instance), /// }, /// } - /// ''' /// ``` String _createSealedFunctionExpressionBody() { assert(element.isSealed); @@ -171,11 +167,9 @@ mixin EncodeHelper implements HelperCore { /// /// For example: /// ```dart - /// ''' /// { /// 'exampleField': instance.exampleField, /// } - /// ''' /// ``` String _createFieldMapFunctionExpressionBody( Set accessibleFields, diff --git a/json_serializable/lib/src/utils.dart b/json_serializable/lib/src/utils.dart index 58ac8fd4..5670061f 100644 --- a/json_serializable/lib/src/utils.dart +++ b/json_serializable/lib/src/utils.dart @@ -178,7 +178,7 @@ Iterable sealedClassImplementations( .where((e) => e.allSupertypes.contains(sc.thisType)); } - return []; + return const Iterable.empty(); } /// If [targetType] is an enum, returns the [FieldElement] instances associated From 4ca71f7741e2d9ff9fb34e45545371f05c098fdd Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Fri, 4 Apr 2025 19:19:56 +0300 Subject: [PATCH 05/18] fix: fix existing tests --- .../lib/src/json_serializable.dart | 40 +++++++++---------- .../lib/src/json_serializable.g.dart | 28 ++++++------- json_serializable/README.md | 2 + .../lib/src/type_helpers/config_types.dart | 24 +++++------ json_serializable/lib/src/utils.dart | 10 ++--- json_serializable/test/config_test.dart | 16 +++++++- json_serializable/test/shared_config.dart | 2 + .../test/test_sources/test_sources.dart | 2 + .../tool/readme/readme_template.md | 2 + 9 files changed, 74 insertions(+), 52 deletions(-) diff --git a/json_annotation/lib/src/json_serializable.dart b/json_annotation/lib/src/json_serializable.dart index dac18f53..933ce083 100644 --- a/json_annotation/lib/src/json_serializable.dart +++ b/json_annotation/lib/src/json_serializable.dart @@ -182,20 +182,6 @@ class JsonSerializable { /// fields annotated with [JsonKey]. final FieldRename? fieldRename; - /// Defines the automatic naming strategy when converting class names - /// to union type names. - /// - /// With a value [UnionRename.none] (the default), the name of the class is - /// used without modification. - /// - /// See [UnionRename] for details on the other options. - final UnionRename? unionRename; - - /// The discriminator key used to identify the union type. - /// - /// Defaults to `type`. - final String? unionDiscriminator; - /// When `true` on classes with type parameters (generic types), extra /// "helper" parameters will be generated for `fromJson` and/or `toJson` to /// support serializing values of those types. @@ -258,6 +244,20 @@ class JsonSerializable { /// `includeIfNull`, that value takes precedent. final bool? includeIfNull; + /// The discriminator key used to identify the union type. + /// + /// Defaults to `type`. + final String? unionDiscriminator; + + /// Defines the automatic naming strategy when converting class names + /// to union type names. + /// + /// With a value [UnionRename.none] (the default), the name of the class is + /// used without modification. + /// + /// See [UnionRename] for details on the other options. + final UnionRename? unionRename; + /// A list of [JsonConverter] to apply to this class. /// /// Writing: @@ -305,13 +305,13 @@ class JsonSerializable { this.disallowUnrecognizedKeys, this.explicitToJson, this.fieldRename, - this.unionRename, - this.unionDiscriminator, this.ignoreUnannotated, this.includeIfNull, this.converters, this.genericArgumentFactories, this.createPerFieldToJson, + this.unionDiscriminator, + this.unionRename, }); factory JsonSerializable.fromJson(Map json) => @@ -329,11 +329,11 @@ class JsonSerializable { disallowUnrecognizedKeys: false, explicitToJson: false, fieldRename: FieldRename.none, - unionRename: UnionRename.none, - unionDiscriminator: 'type', ignoreUnannotated: false, includeIfNull: true, genericArgumentFactories: false, + unionDiscriminator: 'type', + unionRename: UnionRename.none, ); /// Returns a new [JsonSerializable] instance with fields equal to the @@ -352,12 +352,12 @@ class JsonSerializable { disallowUnrecognizedKeys ?? defaults.disallowUnrecognizedKeys, explicitToJson: explicitToJson ?? defaults.explicitToJson, fieldRename: fieldRename ?? defaults.fieldRename, - unionRename: unionRename ?? defaults.unionRename, - unionDiscriminator: unionDiscriminator ?? defaults.unionDiscriminator, ignoreUnannotated: ignoreUnannotated ?? defaults.ignoreUnannotated, includeIfNull: includeIfNull ?? defaults.includeIfNull, genericArgumentFactories: genericArgumentFactories ?? defaults.genericArgumentFactories, + unionDiscriminator: unionDiscriminator ?? defaults.unionDiscriminator, + unionRename: unionRename ?? defaults.unionRename, ); Map toJson() => _$JsonSerializableToJson(this); diff --git a/json_annotation/lib/src/json_serializable.g.dart b/json_annotation/lib/src/json_serializable.g.dart index 1772c254..c6ad60b6 100644 --- a/json_annotation/lib/src/json_serializable.g.dart +++ b/json_annotation/lib/src/json_serializable.g.dart @@ -26,11 +26,11 @@ JsonSerializable _$JsonSerializableFromJson( 'disallow_unrecognized_keys', 'explicit_to_json', 'field_rename', - 'union_rename', - 'union_discriminator', 'generic_argument_factories', 'ignore_unannotated', 'include_if_null', + 'union_discriminator', + 'union_rename', ], ); final val = JsonSerializable( @@ -50,14 +50,6 @@ JsonSerializable _$JsonSerializableFromJson( 'field_rename', (v) => $enumDecodeNullable(_$FieldRenameEnumMap, v), ), - unionRename: $checkedConvert( - 'union_rename', - (v) => $enumDecodeNullable(_$UnionRenameEnumMap, v), - ), - unionDiscriminator: $checkedConvert( - 'union_discriminator', - (v) => v as String?, - ), ignoreUnannotated: $checkedConvert( 'ignore_unannotated', (v) => v as bool?, @@ -71,6 +63,14 @@ JsonSerializable _$JsonSerializableFromJson( 'create_per_field_to_json', (v) => v as bool?, ), + unionDiscriminator: $checkedConvert( + 'union_discriminator', + (v) => v as String?, + ), + unionRename: $checkedConvert( + 'union_rename', + (v) => $enumDecodeNullable(_$UnionRenameEnumMap, v), + ), ); return val; }, @@ -83,12 +83,12 @@ JsonSerializable _$JsonSerializableFromJson( 'disallowUnrecognizedKeys': 'disallow_unrecognized_keys', 'explicitToJson': 'explicit_to_json', 'fieldRename': 'field_rename', - 'unionRename': 'union_rename', - 'unionDiscriminator': 'union_discriminator', 'ignoreUnannotated': 'ignore_unannotated', 'includeIfNull': 'include_if_null', 'genericArgumentFactories': 'generic_argument_factories', 'createPerFieldToJson': 'create_per_field_to_json', + 'unionDiscriminator': 'union_discriminator', + 'unionRename': 'union_rename', }, ); @@ -105,11 +105,11 @@ Map _$JsonSerializableToJson(JsonSerializable instance) => 'disallow_unrecognized_keys': instance.disallowUnrecognizedKeys, 'explicit_to_json': instance.explicitToJson, 'field_rename': _$FieldRenameEnumMap[instance.fieldRename], - 'union_rename': _$UnionRenameEnumMap[instance.unionRename], - 'union_discriminator': instance.unionDiscriminator, 'generic_argument_factories': instance.genericArgumentFactories, 'ignore_unannotated': instance.ignoreUnannotated, 'include_if_null': instance.includeIfNull, + 'union_discriminator': instance.unionDiscriminator, + 'union_rename': _$UnionRenameEnumMap[instance.unionRename], }; const _$FieldRenameEnumMap = { diff --git a/json_serializable/README.md b/json_serializable/README.md index 47c0d0cb..e31c6dc1 100644 --- a/json_serializable/README.md +++ b/json_serializable/README.md @@ -282,6 +282,8 @@ targets: generic_argument_factories: false ignore_unannotated: false include_if_null: true + union_discriminator: type + union_rename: none ``` To exclude generated files from coverage, you can further configure `build.yaml`. diff --git a/json_serializable/lib/src/type_helpers/config_types.dart b/json_serializable/lib/src/type_helpers/config_types.dart index 6ebf52a8..9d6002d5 100644 --- a/json_serializable/lib/src/type_helpers/config_types.dart +++ b/json_serializable/lib/src/type_helpers/config_types.dart @@ -54,13 +54,13 @@ class ClassConfig { final bool disallowUnrecognizedKeys; final bool explicitToJson; final FieldRename fieldRename; - final UnionRename unionRename; - final String unionDiscriminator; final bool genericArgumentFactories; final bool ignoreUnannotated; final bool includeIfNull; final Map ctorParamDefaults; final List converters; + final String unionDiscriminator; + final UnionRename unionRename; const ClassConfig({ required this.anyMap, @@ -74,11 +74,11 @@ class ClassConfig { required this.disallowUnrecognizedKeys, required this.explicitToJson, required this.fieldRename, - required this.unionRename, - required this.unionDiscriminator, required this.genericArgumentFactories, required this.ignoreUnannotated, required this.includeIfNull, + required this.unionDiscriminator, + required this.unionRename, this.converters = const [], this.ctorParamDefaults = const {}, }); @@ -109,13 +109,13 @@ class ClassConfig { config.genericArgumentFactories ?? ClassConfig.defaults.genericArgumentFactories, fieldRename: config.fieldRename ?? ClassConfig.defaults.fieldRename, - unionRename: config.unionRename ?? ClassConfig.defaults.unionRename, - unionDiscriminator: - config.unionDiscriminator ?? - ClassConfig.defaults.unionDiscriminator, disallowUnrecognizedKeys: config.disallowUnrecognizedKeys ?? ClassConfig.defaults.disallowUnrecognizedKeys, + unionDiscriminator: + config.unionDiscriminator ?? + ClassConfig.defaults.unionDiscriminator, + unionRename: config.unionRename ?? ClassConfig.defaults.unionRename, // TODO typeConverters = [] ); @@ -133,11 +133,11 @@ class ClassConfig { disallowUnrecognizedKeys: false, explicitToJson: false, fieldRename: FieldRename.none, - unionRename: UnionRename.none, - unionDiscriminator: 'type', genericArgumentFactories: false, ignoreUnannotated: false, includeIfNull: true, + unionDiscriminator: 'type', + unionRename: UnionRename.none, ); JsonSerializable toJsonSerializable() => JsonSerializable( @@ -154,9 +154,9 @@ class ClassConfig { includeIfNull: includeIfNull, genericArgumentFactories: genericArgumentFactories, fieldRename: fieldRename, - unionRename: unionRename, - unionDiscriminator: unionDiscriminator, disallowUnrecognizedKeys: disallowUnrecognizedKeys, + unionDiscriminator: unionDiscriminator, + unionRename: unionRename, // TODO typeConverters = [] ); } diff --git a/json_serializable/lib/src/utils.dart b/json_serializable/lib/src/utils.dart index 5670061f..d763ba9f 100644 --- a/json_serializable/lib/src/utils.dart +++ b/json_serializable/lib/src/utils.dart @@ -61,12 +61,12 @@ JsonSerializable _valueForAnnotation(ConstantReader reader) => JsonSerializable( reader.read('disallowUnrecognizedKeys').literalValue as bool?, explicitToJson: reader.read('explicitToJson').literalValue as bool?, fieldRename: readEnum(reader.read('fieldRename'), FieldRename.values), - unionRename: readEnum(reader.read('unionRename'), UnionRename.values), - unionDiscriminator: reader.read('unionDiscriminator').literalValue as String?, genericArgumentFactories: reader.read('genericArgumentFactories').literalValue as bool?, ignoreUnannotated: reader.read('ignoreUnannotated').literalValue as bool?, includeIfNull: reader.read('includeIfNull').literalValue as bool?, + unionDiscriminator: reader.read('unionDiscriminator').literalValue as String?, + unionRename: readEnum(reader.read('unionRename'), UnionRename.values), ); /// Returns a [ClassConfig] with values from the [JsonSerializable] @@ -116,9 +116,6 @@ ClassConfig mergeConfig( annotation.disallowUnrecognizedKeys ?? config.disallowUnrecognizedKeys, explicitToJson: annotation.explicitToJson ?? config.explicitToJson, fieldRename: annotation.fieldRename ?? config.fieldRename, - unionRename: annotation.unionRename ?? config.unionRename, - unionDiscriminator: - annotation.unionDiscriminator ?? config.unionDiscriminator, genericArgumentFactories: annotation.genericArgumentFactories ?? (classElement.typeParameters.isNotEmpty && @@ -127,6 +124,9 @@ ClassConfig mergeConfig( includeIfNull: annotation.includeIfNull ?? config.includeIfNull, ctorParamDefaults: paramDefaultValueMap, converters: converters.isNull ? const [] : converters.listValue, + unionDiscriminator: + annotation.unionDiscriminator ?? config.unionDiscriminator, + unionRename: annotation.unionRename ?? config.unionRename, ); } diff --git a/json_serializable/test/config_test.dart b/json_serializable/test/config_test.dart index b29ffac1..c9f135c6 100644 --- a/json_serializable/test/config_test.dart +++ b/json_serializable/test/config_test.dart @@ -43,6 +43,12 @@ void main() { ); for (var entry in generatorConfigDefaultJson.entries) { + expect( + generatorConfigNonDefaultJson[entry.key], + isNotNull, + reason: 'should have explicitly set non default value', + ); + expect( generatorConfigNonDefaultJson, containsPair(entry.key, isNot(entry.value)), @@ -91,7 +97,7 @@ void main() { configMap.keys, unorderedEquals(generatorConfigDefaultJson.keys), reason: - 'All supported keys are documented. ' + 'All supported keys are not documented. ' 'Did you forget to change README.md?', ); @@ -133,6 +139,12 @@ void main() { 'field_rename' => '`42` is not one of the supported values: none, kebab, snake, ' 'pascal, screamingSnake', + 'union_rename' => + '`42` is not one of the supported values: none, kebab, snake, ' + 'pascal, screamingSnake', + 'union_discriminator' => + "type 'int' is not a subtype of type 'String?' in type " + 'cast', 'constructor' => "type 'int' is not a subtype of type 'String?' in type " 'cast', @@ -175,4 +187,6 @@ const _invalidConfig = { 'generic_argument_factories': 42, 'ignore_unannotated': 42, 'include_if_null': 42, + 'union_discriminator': 42, + 'union_rename': 42, }; diff --git a/json_serializable/test/shared_config.dart b/json_serializable/test/shared_config.dart index 51025017..8bf46616 100644 --- a/json_serializable/test/shared_config.dart +++ b/json_serializable/test/shared_config.dart @@ -28,5 +28,7 @@ final generatorConfigNonDefaultJson = Map.unmodifiable( ignoreUnannotated: true, includeIfNull: false, genericArgumentFactories: true, + unionDiscriminator: 'runtimeType', + unionRename: UnionRename.kebab, ).toJson(), ); diff --git a/json_serializable/test/test_sources/test_sources.dart b/json_serializable/test/test_sources/test_sources.dart index 80a08aa3..959edeed 100644 --- a/json_serializable/test/test_sources/test_sources.dart +++ b/json_serializable/test/test_sources/test_sources.dart @@ -24,6 +24,8 @@ class ConfigurationImplicitDefaults { ignoreUnannotated: false, includeIfNull: true, genericArgumentFactories: false, + unionDiscriminator: 'type', + unionRename: UnionRename.none, ) class ConfigurationExplicitDefaults { int? field; diff --git a/json_serializable/tool/readme/readme_template.md b/json_serializable/tool/readme/readme_template.md index d99c3ec7..3dacbcca 100644 --- a/json_serializable/tool/readme/readme_template.md +++ b/json_serializable/tool/readme/readme_template.md @@ -150,6 +150,8 @@ targets: generic_argument_factories: false ignore_unannotated: false include_if_null: true + union_discriminator: type + union_rename: none ``` To exclude generated files from coverage, you can further configure `build.yaml`. From 9acdce5ab8cf8657022c9a531e6d4973627bd1ae Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Sat, 5 Apr 2025 15:19:33 +0300 Subject: [PATCH 06/18] feat: throw on invalid config and add support for complex sealed classes --- .../lib/complex_sealed_class_examples.dart | 79 ++++++++++++ .../lib/complex_sealed_class_examples.g.dart | 121 ++++++++++++++++++ .../lib/src/generator_helper.dart | 61 +++++++++ json_serializable/lib/src/utils.dart | 59 ++++++++- 4 files changed, 313 insertions(+), 7 deletions(-) create mode 100644 example/lib/complex_sealed_class_examples.dart create mode 100644 example/lib/complex_sealed_class_examples.g.dart diff --git a/example/lib/complex_sealed_class_examples.dart b/example/lib/complex_sealed_class_examples.dart new file mode 100644 index 00000000..f66a6d71 --- /dev/null +++ b/example/lib/complex_sealed_class_examples.dart @@ -0,0 +1,79 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'complex_sealed_class_examples.g.dart'; + +@JsonSerializable( + unionDiscriminator: 'base_base_base_type', +) +sealed class BaseBaseBaseType { + BaseBaseBaseType(); + + factory BaseBaseBaseType.fromJson(Map json) => + _$BaseBaseBaseTypeFromJson(json); + + Map toJson() => _$BaseBaseBaseTypeToJson(this); +} + +@JsonSerializable( + unionDiscriminator: 'base_base_type', +) +sealed class BaseBase extends BaseBaseBaseType { + BaseBase(); +} + +@JsonSerializable( + unionDiscriminator: 'base_type', +) +sealed class Base extends BaseBase { + Base(); +} + +@JsonSerializable() +class FirstBaseImpl extends Base { + FirstBaseImpl(this.value); + + String value; +} + +@JsonSerializable() +class SecondBaseImpl extends Base { + SecondBaseImpl(this.value); + + String value; +} + +@JsonSerializable() +class BaseBaseImpl extends BaseBase { + BaseBaseImpl(this.value); + + String value; +} + +@JsonSerializable( + createToJson: false, +) +sealed class SecondBase { + SecondBase(); + + factory SecondBase.fromJson(Map json) => + _$SecondBaseFromJson(json); +} + +@JsonSerializable( + createToJson: false, +) +sealed class ThirdBase { + ThirdBase(); + + factory ThirdBase.fromJson(Map json) => + _$ThirdBaseFromJson(json); +} + +@JsonSerializable( + createToJson: false, +) +class ImplAll implements SecondBase, ThirdBase { + ImplAll(this.value); + + String value; +} diff --git a/example/lib/complex_sealed_class_examples.g.dart b/example/lib/complex_sealed_class_examples.g.dart new file mode 100644 index 00000000..df33bbb1 --- /dev/null +++ b/example/lib/complex_sealed_class_examples.g.dart @@ -0,0 +1,121 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'complex_sealed_class_examples.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BaseBaseBaseType _$BaseBaseBaseTypeFromJson(Map json) => + switch (json['base_base_base_type']) { + 'BaseBase' => _$BaseBaseFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['base_base_base_type']}', + BaseBaseBaseType, + json, + ), + }; + +Map _$BaseBaseBaseTypeToJson(BaseBaseBaseType instance) => + switch (instance) { + final BaseBase instance => { + 'base_base_base_type': 'BaseBase', + ..._$BaseBaseToJson(instance), + }, + }; + +BaseBase _$BaseBaseFromJson(Map json) => + switch (json['base_base_type']) { + 'Base' => _$BaseFromJson(json), + 'BaseBaseImpl' => _$BaseBaseImplFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['base_base_type']}', + BaseBase, + json, + ), + }; + +Map _$BaseBaseToJson(BaseBase instance) => switch (instance) { + final Base instance => { + 'base_base_type': 'Base', + ..._$BaseToJson(instance), + }, + final BaseBaseImpl instance => { + 'base_base_type': 'BaseBaseImpl', + ..._$BaseBaseImplToJson(instance), + }, + }; + +Base _$BaseFromJson(Map json) => switch (json['base_type']) { + 'FirstBaseImpl' => _$FirstBaseImplFromJson(json), + 'SecondBaseImpl' => _$SecondBaseImplFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['base_type']}', + Base, + json, + ), + }; + +Map _$BaseToJson(Base instance) => switch (instance) { + final FirstBaseImpl instance => { + 'base_type': 'FirstBaseImpl', + ..._$FirstBaseImplToJson(instance), + }, + final SecondBaseImpl instance => { + 'base_type': 'SecondBaseImpl', + ..._$SecondBaseImplToJson(instance), + }, + }; + +FirstBaseImpl _$FirstBaseImplFromJson(Map json) => + FirstBaseImpl( + json['value'] as String, + ); + +Map _$FirstBaseImplToJson(FirstBaseImpl instance) => + { + 'value': instance.value, + }; + +SecondBaseImpl _$SecondBaseImplFromJson(Map json) => + SecondBaseImpl( + json['value'] as String, + ); + +Map _$SecondBaseImplToJson(SecondBaseImpl instance) => + { + 'value': instance.value, + }; + +BaseBaseImpl _$BaseBaseImplFromJson(Map json) => BaseBaseImpl( + json['value'] as String, + ); + +Map _$BaseBaseImplToJson(BaseBaseImpl instance) => + { + 'value': instance.value, + }; + +SecondBase _$SecondBaseFromJson(Map json) => + switch (json['type']) { + 'ImplAll' => _$ImplAllFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SecondBase, + json, + ), + }; + +ThirdBase _$ThirdBaseFromJson(Map json) => + switch (json['type']) { + 'ImplAll' => _$ImplAllFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + ThirdBase, + json, + ), + }; + +ImplAll _$ImplAllFromJson(Map json) => ImplAll( + json['value'] as String, + ); diff --git a/json_serializable/lib/src/generator_helper.dart b/json_serializable/lib/src/generator_helper.dart index ef1bf16e..91e04fa5 100644 --- a/json_serializable/lib/src/generator_helper.dart +++ b/json_serializable/lib/src/generator_helper.dart @@ -48,6 +48,57 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ); } + final sealedSuperClassesOrEmpty = sealedSuperClasses(element); + + final sealedDiscriminators = sealedSuperClassesOrEmpty + .map((sealedClass) => jsonSerializableConfig(sealedClass, _generator)) + .map((config) => config?.unionDiscriminator); + + if ((sealedSuperClassesOrEmpty.isNotEmpty || element.isSealed) && + config.genericArgumentFactories) { + throw InvalidGenerationSourceError( + 'The class `${element.displayName}` is annotated ' + 'with `JsonSerializable` field `genericArgumentFactories: true`. ' + '`genericArgumentFactories: true` is not supported for classes ' + 'that are sealed or have sealed superclasses.', + todo: + 'Remove the `genericArgumentFactories` option or ' + 'remove the `sealed` keyword from the class.', + element: element, + ); + } + + if (element.isSealed) { + if (sealedDiscriminators.contains(config.unionDiscriminator)) { + throw InvalidGenerationSource( + 'Nested sealed classes cannot have the same discriminator.', + todo: + 'Rename one of the discriminators with `unionDiscriminator` ' + 'field in `@JsonSerializable`.', + ); + } + sealedClassImplementations(element).forEach((impl) { + final annotationConfig = jsonSerializableConfig(impl, _generator); + + if (annotationConfig == null) { + throw InvalidGenerationSourceError( + 'The class `${element.displayName}` is sealed but its ' + 'implementation `${impl.displayName}` is not annotated with ' + '`JsonSerializable`.', + todo: 'Add `@JsonSerializable` annotation to ${impl.displayName}.', + ); + } + + if (annotationConfig.createToJson != config.createToJson) { + throw InvalidGenerationSourceError( + 'The class `${element.displayName}` is sealed but its ' + 'implementation `${impl.displayName}` has a different ' + '`createToJson` option than the base class.', + ); + } + }); + } + final sortedFields = createSortedFieldSet(element); // Used to keep track of why a field is ignored. Useful for providing @@ -113,6 +164,16 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { // by `_writeCtor`. ..fold({}, (Set set, fe) { final jsonKey = nameAccess(fe); + + if (sealedDiscriminators.contains(jsonKey)) { + throw InvalidGenerationSourceError( + 'The JSON key "$jsonKey" is conflicting with the discriminator ' + 'of sealed superclass ', + todo: 'Rename the field or the discriminator.', + element: fe, + ); + } + if (!set.add(jsonKey)) { throw InvalidGenerationSourceError( 'More than one field has the JSON key for name "$jsonKey".', diff --git a/json_serializable/lib/src/utils.dart b/json_serializable/lib/src/utils.dart index d763ba9f..e47be73f 100644 --- a/json_serializable/lib/src/utils.dart +++ b/json_serializable/lib/src/utils.dart @@ -9,10 +9,12 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:source_gen/source_gen.dart'; import 'package:source_helper/source_helper.dart'; +import 'settings.dart'; import 'shared_checkers.dart'; import 'type_helpers/config_types.dart'; const _jsonKeyChecker = TypeChecker.fromRuntime(JsonKey); +const _jsonSerializableChecker = TypeChecker.fromRuntime(JsonSerializable); DartObject? _jsonKeyAnnotation(FieldElement element) => _jsonKeyChecker.firstAnnotationOf(element) ?? @@ -167,20 +169,63 @@ ConstructorElement constructorByName(ClassElement classElement, String name) { return ctor; } +/// Given a [ClassElement] that is a sealed class, returns all the +/// direct subclasses of the given sealed class, excluding any +/// indirect subclasses (ie. subclasses of subclasses). +/// +/// Otherwise, returns an empty iterable. Iterable sealedClassImplementations( - ClassElement maybeSealedClass, + ClassElement maybeSealedSuperClass, ) { - if (maybeSealedClass case final sc when sc.isSealed) { - return LibraryReader(sc.library) - .annotatedWith(const TypeChecker.fromRuntime(JsonSerializable)) - .map((e) => e.element) - .whereType() - .where((e) => e.allSupertypes.contains(sc.thisType)); + if (maybeSealedSuperClass case final sc when sc.isSealed) { + return LibraryReader( + sc.library, + ).allElements.whereType().where( + (e) => e.interfaces.contains(sc.thisType) || e.supertype?.element == sc, + ); } return const Iterable.empty(); } +/// Given a [ClassElement] that is a subclass of sealed classes, returns +/// all of the sealed superclasses, including all indirect superclasses +/// (ie. superclasses of superclasses) +/// +/// Otherwise, returns an empty iterable. +Iterable sealedSuperClasses( + ClassElement maybeSealedImplementation, +) => maybeSealedImplementation.allSupertypes + .map((type) => type.element) + .whereType() + .where((element) => element.isSealed); + +/// Given a [ClassElement] that is annotated with `@JsonSerializable`, returns +/// the annotation config merged with build runner config and defaults. +/// +/// Otherwise, returns `null`. +ClassConfig? jsonSerializableConfig( + ClassElement maybeAnnotatedElement, + Settings generator, +) { + final maybeSuperAnnotation = _jsonSerializableChecker.firstAnnotationOfExact( + maybeAnnotatedElement, + throwOnUnresolved: false, + ); + + if (maybeSuperAnnotation case final superAnnotation?) { + final annotationReader = ConstantReader(superAnnotation); + + return mergeConfig( + generator.config, + annotationReader, + classElement: maybeAnnotatedElement, + ); + } + + return null; +} + /// If [targetType] is an enum, returns the [FieldElement] instances associated /// with its values. /// From 7ae5d4465b986224e860ed5f1d8b66668cc7234e Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Sun, 6 Apr 2025 16:16:29 +0300 Subject: [PATCH 07/18] fix: consistency improvements and more invalid config validations --- json_serializable/lib/src/decode_helper.dart | 2 +- json_serializable/lib/src/encoder_helper.dart | 2 +- .../lib/src/generator_helper.dart | 78 ++++++++++++------- json_serializable/lib/src/utils.dart | 2 +- 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/json_serializable/lib/src/decode_helper.dart b/json_serializable/lib/src/decode_helper.dart index 604015db..8de762db 100644 --- a/json_serializable/lib/src/decode_helper.dart +++ b/json_serializable/lib/src/decode_helper.dart @@ -141,7 +141,7 @@ mixin DecodeHelper implements HelperCore { String _createSealedFunctionExpressionBody() { assert(element.isSealed); - final implementations = sealedClassImplementations(element); + final implementations = sealedSubClasses(element); final discriminator = config.unionDiscriminator; diff --git a/json_serializable/lib/src/encoder_helper.dart b/json_serializable/lib/src/encoder_helper.dart index 36b986fc..50579a1f 100644 --- a/json_serializable/lib/src/encoder_helper.dart +++ b/json_serializable/lib/src/encoder_helper.dart @@ -138,7 +138,7 @@ mixin EncodeHelper implements HelperCore { String _createSealedFunctionExpressionBody() { assert(element.isSealed); - final implementations = sealedClassImplementations(element); + final implementations = sealedSubClasses(element); final discriminator = config.unionDiscriminator; diff --git a/json_serializable/lib/src/generator_helper.dart b/json_serializable/lib/src/generator_helper.dart index 91e04fa5..c8aacdf3 100644 --- a/json_serializable/lib/src/generator_helper.dart +++ b/json_serializable/lib/src/generator_helper.dart @@ -4,6 +4,7 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:build/build.dart'; +import 'package:collection/collection.dart'; import 'package:source_gen/source_gen.dart'; import '../type_helper.dart'; @@ -48,13 +49,25 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ); } - final sealedSuperClassesOrEmpty = sealedSuperClasses(element); + final sealedSupersAndConfigs = sealedSuperClasses(element).map( + (superClass) => ( + classElement: superClass, + config: jsonSerializableConfig(superClass, _generator), + ), + ); - final sealedDiscriminators = sealedSuperClassesOrEmpty - .map((sealedClass) => jsonSerializableConfig(sealedClass, _generator)) - .map((config) => config?.unionDiscriminator); + if (sealedSupersAndConfigs.isNotEmpty && + sealedSupersAndConfigs.any((e) => e.config == null)) { + throw InvalidGenerationSourceError( + 'The class `${element.displayName}` is annotated ' + 'with `JsonSerializable` but its superclass is not annotated ' + 'with `JsonSerializable`.', + todo: 'Add `@JsonSerializable` annotation to the sealed class.', + element: element, + ); + } - if ((sealedSuperClassesOrEmpty.isNotEmpty || element.isSealed) && + if ((sealedSupersAndConfigs.isNotEmpty || element.isSealed) && config.genericArgumentFactories) { throw InvalidGenerationSourceError( 'The class `${element.displayName}` is annotated ' @@ -68,32 +81,43 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ); } + if (sealedSupersAndConfigs.firstWhereOrNull( + (e) => e.config?.unionDiscriminator == config.unionDiscriminator, + ) + case final conflictingSuper? when element.isSealed) { + throw InvalidGenerationSource( + 'The classes `${conflictingSuper.classElement.displayName}` and ' + '${element.displayName} are nested sealed classes, but they have ' + 'the same discriminator ${config.unionDiscriminator}.', + todo: + 'Rename one of the discriminators with `unionDiscriminator` ' + 'field of `@JsonSerializable`.', + ); + } + + if (sealedSupersAndConfigs.firstWhereOrNull( + (e) => e.config?.createToJson != config.createToJson, + ) + case final diffSuper?) { + throw InvalidGenerationSourceError( + 'The class `${diffSuper.classElement.displayName}` is sealed but its ' + 'subclass `${element.displayName}` has a different ' + '`createToJson` option than the base class.', + element: element, + ); + } + if (element.isSealed) { - if (sealedDiscriminators.contains(config.unionDiscriminator)) { - throw InvalidGenerationSource( - 'Nested sealed classes cannot have the same discriminator.', - todo: - 'Rename one of the discriminators with `unionDiscriminator` ' - 'field in `@JsonSerializable`.', - ); - } - sealedClassImplementations(element).forEach((impl) { - final annotationConfig = jsonSerializableConfig(impl, _generator); + sealedSubClasses(element).forEach((sub) { + final annotationConfig = jsonSerializableConfig(sub, _generator); if (annotationConfig == null) { throw InvalidGenerationSourceError( 'The class `${element.displayName}` is sealed but its ' - 'implementation `${impl.displayName}` is not annotated with ' + 'subclass `${sub.displayName}` is not annotated with ' '`JsonSerializable`.', - todo: 'Add `@JsonSerializable` annotation to ${impl.displayName}.', - ); - } - - if (annotationConfig.createToJson != config.createToJson) { - throw InvalidGenerationSourceError( - 'The class `${element.displayName}` is sealed but its ' - 'implementation `${impl.displayName}` has a different ' - '`createToJson` option than the base class.', + todo: 'Add `@JsonSerializable` annotation to ${sub.displayName}.', + element: sub, ); } }); @@ -165,7 +189,9 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ..fold({}, (Set set, fe) { final jsonKey = nameAccess(fe); - if (sealedDiscriminators.contains(jsonKey)) { + if (sealedSupersAndConfigs.any( + (e) => e.config?.unionDiscriminator == jsonKey, + )) { throw InvalidGenerationSourceError( 'The JSON key "$jsonKey" is conflicting with the discriminator ' 'of sealed superclass ', diff --git a/json_serializable/lib/src/utils.dart b/json_serializable/lib/src/utils.dart index e47be73f..c82f1fa7 100644 --- a/json_serializable/lib/src/utils.dart +++ b/json_serializable/lib/src/utils.dart @@ -174,7 +174,7 @@ ConstructorElement constructorByName(ClassElement classElement, String name) { /// indirect subclasses (ie. subclasses of subclasses). /// /// Otherwise, returns an empty iterable. -Iterable sealedClassImplementations( +Iterable sealedSubClasses( ClassElement maybeSealedSuperClass, ) { if (maybeSealedSuperClass case final sc when sc.isSealed) { From 5a8713880b6be48698365ca2226ddeadf1965e22 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Sun, 6 Apr 2025 20:28:36 +0300 Subject: [PATCH 08/18] test: add tests for new features --- .../lib/src/generator_helper.dart | 23 +- .../test/json_serializable_test.dart | 63 +- .../src/_json_serializable_test_input.dart | 5 + .../src/conflicting_discriminator_input.dart | 119 +++ .../test/src/generic_test_input.dart | 79 ++ .../test/src/mismatching_config_input.dart | 45 + .../test/src/missing_annotation_input.dart | 56 ++ .../test/src/sealed_test_input.dart | 779 ++++++++++++++++++ .../test/src/union_namer_input.dart | 180 ++++ 9 files changed, 1338 insertions(+), 11 deletions(-) create mode 100644 json_serializable/test/src/conflicting_discriminator_input.dart create mode 100644 json_serializable/test/src/mismatching_config_input.dart create mode 100644 json_serializable/test/src/missing_annotation_input.dart create mode 100644 json_serializable/test/src/sealed_test_input.dart create mode 100644 json_serializable/test/src/union_namer_input.dart diff --git a/json_serializable/lib/src/generator_helper.dart b/json_serializable/lib/src/generator_helper.dart index c8aacdf3..63153459 100644 --- a/json_serializable/lib/src/generator_helper.dart +++ b/json_serializable/lib/src/generator_helper.dart @@ -56,11 +56,12 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ), ); - if (sealedSupersAndConfigs.isNotEmpty && - sealedSupersAndConfigs.any((e) => e.config == null)) { + if (sealedSupersAndConfigs.firstWhereOrNull((e) => e.config == null) + case final notAnnotated? when sealedSupersAndConfigs.isNotEmpty) { throw InvalidGenerationSourceError( 'The class `${element.displayName}` is annotated ' - 'with `JsonSerializable` but its superclass is not annotated ' + 'with `JsonSerializable` but its sealed superclass ' + '`${notAnnotated.classElement.displayName}` is not annotated ' 'with `JsonSerializable`.', todo: 'Add `@JsonSerializable` annotation to the sealed class.', element: element, @@ -87,11 +88,12 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { case final conflictingSuper? when element.isSealed) { throw InvalidGenerationSource( 'The classes `${conflictingSuper.classElement.displayName}` and ' - '${element.displayName} are nested sealed classes, but they have ' - 'the same discriminator ${config.unionDiscriminator}.', + '`${element.displayName}` are nested sealed classes, but they have ' + 'the same discriminator `${config.unionDiscriminator}`.', todo: 'Rename one of the discriminators with `unionDiscriminator` ' 'field of `@JsonSerializable`.', + element: element, ); } @@ -189,12 +191,13 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ..fold({}, (Set set, fe) { final jsonKey = nameAccess(fe); - if (sealedSupersAndConfigs.any( - (e) => e.config?.unionDiscriminator == jsonKey, - )) { + if (sealedSupersAndConfigs.firstWhereOrNull( + (e) => e.config?.unionDiscriminator == jsonKey, + ) + case final conflict?) { throw InvalidGenerationSourceError( - 'The JSON key "$jsonKey" is conflicting with the discriminator ' - 'of sealed superclass ', + 'The JSON key `$jsonKey` is conflicting with the discriminator ' + 'of sealed superclass `${conflict.classElement.displayName}`', todo: 'Rename the field or the discriminator.', element: fe, ); diff --git a/json_serializable/test/json_serializable_test.dart b/json_serializable/test/json_serializable_test.dart index b6ff3678..ef562246 100644 --- a/json_serializable/test/json_serializable_test.dart +++ b/json_serializable/test/json_serializable_test.dart @@ -122,11 +122,72 @@ const _expectedAnnotatedTests = { 'Reproduce869NullableGenericTypeWithDefault', 'SameCtorAndJsonKeyDefaultValue', 'SetSupport', + 'SubFourMultipleImpl', + 'SubNestedOneOne', + 'SubNestedOneTwo', + 'SubNestedTwoOne', + 'SubNestedTwoTwo', + 'SubOneMultipleImpl', + 'SubOneSimpleSealedClass', + 'SubOneSimpleSealedClassWithChangedDiscriminator', + 'SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename', + 'SubOneSimpleSealedClassWithChangedUnionRename', + 'SubSimpleSealedClassWithoutToJson', + 'SubThreeMultipleImpl', + 'SubTwoMultipleImpl', + 'SubTwoSimpleSealedClass', + 'SubTwoSimpleSealedClassWithChangedDiscriminator', + 'SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename', + 'SubTwoSimpleSealedClassWithChangedUnionRename', 'SubType', 'SubTypeWithAnnotatedFieldOverrideExtends', 'SubTypeWithAnnotatedFieldOverrideExtendsWithOverrides', 'SubTypeWithAnnotatedFieldOverrideImplements', + 'SubUnionRenameKebab', + 'SubUnionRenameNone', + 'SubUnionRenamePascal', + 'SubUnionRenameScreamingSnake', + 'SubUnionRenameSnake', + 'SubWithConflictingDiscriminatorWithDefaultNameExt', + 'SubWithConflictingDiscriminatorWithDefaultNameImpl', + 'SubWithConflictingDiscriminatorWithFieldRenameExt', + 'SubWithConflictingDiscriminatorWithFieldRenameImpl', + 'SubWithConflictingDiscriminatorWithJsonKeyExt', + 'SubWithConflictingDiscriminatorWithJsonKeyImpl', + 'SubWithSubAndSuperGenericArgumentFactoriesExt', + 'SubWithSubAndSuperGenericArgumentFactoriesImpl', + 'SubWithSubGenericArgumentFactoriesExt', + 'SubWithSubGenericArgumentFactoriesImpl', + 'SubWithToJsonAndSuperWithoutToJsonExt', + 'SubWithToJsonAndSuperWithoutToJsonImpl', + 'SubWithoutSuperJsonSerializableAnnotationExt', + 'SubWithoutSuperJsonSerializableAnnotationImpl', + 'SubWithoutToJsonAndSuperWithToJsonExt', + 'SubWithoutToJsonAndSuperWithToJsonImpl', 'SubclassedJsonKey', + 'SuperMultipleImplOne', + 'SuperMultipleImplTwo', + 'SuperNestedOneOne', + 'SuperNestedOneTwo', + 'SuperNestedTwoOne', + 'SuperNestedTwoTwo', + 'SuperSimpleSealedClass', + 'SuperSimpleSealedClassWithChangedDiscriminator', + 'SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename', + 'SuperSimpleSealedClassWithChangedUnionRename', + 'SuperSimpleSealedClassWithoutToJson', + 'SuperSuperNestedOne', + 'SuperSuperNestedTwo', + 'SuperSuperSuperNested', + 'SuperUnionRenameKebab', + 'SuperUnionRenameNone', + 'SuperUnionRenamePascal', + 'SuperUnionRenameScreamingSnake', + 'SuperUnionRenameSnake', + 'SuperWithConflictingNestedDiscriminator', + 'SuperWithGenericArgumentFactories', + 'SuperWithSubExtWithoutJsonSerializableAnnotation', + 'SuperWithSubImplWithoutJsonSerializableAnnotation', 'TearOffFromJsonClass', 'ToJsonNullableFalseIncludeIfNullFalse', 'TypedConvertMethods', @@ -148,5 +209,5 @@ const _expectedAnnotatedTests = { 'WrongConstructorNameClass', '_BetterPrivateNames', 'annotatedMethod', - 'theAnswer', + 'theAnswer' }; diff --git a/json_serializable/test/src/_json_serializable_test_input.dart b/json_serializable/test/src/_json_serializable_test_input.dart index 4db90e56..5be9bd3b 100644 --- a/json_serializable/test/src/_json_serializable_test_input.dart +++ b/json_serializable/test/src/_json_serializable_test_input.dart @@ -10,6 +10,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:source_gen_test/annotations.dart'; part 'checked_test_input.dart'; +part 'conflicting_discriminator_input.dart'; part 'constants_copy.dart'; part 'core_subclass_type_input.dart'; part 'default_value_input.dart'; @@ -18,8 +19,12 @@ part 'generic_test_input.dart'; part 'inheritance_test_input.dart'; part 'json_converter_test_input.dart'; part 'map_key_variety_test_input.dart'; +part 'mismatching_config_input.dart'; +part 'missing_annotation_input.dart'; +part 'sealed_test_input.dart'; part 'setter_test_input.dart'; part 'to_from_json_test_input.dart'; +part 'union_namer_input.dart'; part 'unknown_enum_value_test_input.dart'; @ShouldThrow('`@JsonSerializable` can only be used on classes.') diff --git a/json_serializable/test/src/conflicting_discriminator_input.dart b/json_serializable/test/src/conflicting_discriminator_input.dart new file mode 100644 index 00000000..67e3913d --- /dev/null +++ b/json_serializable/test/src/conflicting_discriminator_input.dart @@ -0,0 +1,119 @@ +// @dart=3.8 + +part of '_json_serializable_test_input.dart'; + +@JsonSerializable(unionDiscriminator: 'this_will_conflict') +sealed class SuperWithConflictingSnakeCaseDiscriminator {} + +@ShouldThrow( + 'The JSON key `this_will_conflict` is conflicting with the discriminator ' + 'of sealed superclass `SuperWithConflictingSnakeCaseDiscriminator`', + todo: 'Rename the field or the discriminator.', + element: 'thisWillConflict', +) +@JsonSerializable(fieldRename: FieldRename.snake) +class SubWithConflictingDiscriminatorWithFieldRenameExt + extends SuperWithConflictingSnakeCaseDiscriminator { + final String thisWillConflict; + + SubWithConflictingDiscriminatorWithFieldRenameExt({ + required this.thisWillConflict, + }); +} + +@ShouldThrow( + 'The JSON key `this_will_conflict` is conflicting with the discriminator ' + 'of sealed superclass `SuperWithConflictingSnakeCaseDiscriminator`', + todo: 'Rename the field or the discriminator.', + element: 'thisWillConflict', +) +@JsonSerializable(fieldRename: FieldRename.snake) +class SubWithConflictingDiscriminatorWithFieldRenameImpl + implements SuperWithConflictingSnakeCaseDiscriminator { + final String thisWillConflict; + + SubWithConflictingDiscriminatorWithFieldRenameImpl({ + required this.thisWillConflict, + }); +} + +@ShouldThrow( + 'The JSON key `this_will_conflict` is conflicting with the discriminator ' + 'of sealed superclass `SuperWithConflictingSnakeCaseDiscriminator`', + todo: 'Rename the field or the discriminator.', + element: 'conflict', +) +@JsonSerializable() +class SubWithConflictingDiscriminatorWithJsonKeyExt + extends SuperWithConflictingSnakeCaseDiscriminator { + @JsonKey(name: 'this_will_conflict') + final String conflict; + + SubWithConflictingDiscriminatorWithJsonKeyExt({required this.conflict}); +} + +@ShouldThrow( + 'The JSON key `this_will_conflict` is conflicting with the discriminator ' + 'of sealed superclass `SuperWithConflictingSnakeCaseDiscriminator`', + todo: 'Rename the field or the discriminator.', + element: 'conflict', +) +@JsonSerializable() +class SubWithConflictingDiscriminatorWithJsonKeyImpl + implements SuperWithConflictingSnakeCaseDiscriminator { + @JsonKey(name: 'this_will_conflict') + final String conflict; + + SubWithConflictingDiscriminatorWithJsonKeyImpl({required this.conflict}); +} + +@JsonSerializable(unionDiscriminator: 'conflict') +sealed class SuperWithConflictingDefaultCaseDiscriminator {} + +@ShouldThrow( + 'The JSON key `conflict` is conflicting with the discriminator ' + 'of sealed superclass `SuperWithConflictingDefaultCaseDiscriminator`', + todo: 'Rename the field or the discriminator.', + element: 'conflict', +) +@JsonSerializable() +class SubWithConflictingDiscriminatorWithDefaultNameExt + extends SuperWithConflictingDefaultCaseDiscriminator { + final String conflict; + + SubWithConflictingDiscriminatorWithDefaultNameExt({required this.conflict}); +} + +@ShouldThrow( + 'The JSON key `conflict` is conflicting with the discriminator ' + 'of sealed superclass `SuperWithConflictingDefaultCaseDiscriminator`', + todo: 'Rename the field or the discriminator.', + element: 'conflict', +) +@JsonSerializable() +class SubWithConflictingDiscriminatorWithDefaultNameImpl + extends SuperWithConflictingDefaultCaseDiscriminator { + final String conflict; + + SubWithConflictingDiscriminatorWithDefaultNameImpl({required this.conflict}); +} + +@JsonSerializable(unionDiscriminator: 'this_will_conflict') +sealed class SuperSuperSuperWithConflictingNestedDiscriminator {} + +@JsonSerializable(unionDiscriminator: 'this_is_fine') +sealed class SuperSuperWithConflictingNestedDiscriminator + extends SuperSuperSuperWithConflictingNestedDiscriminator {} + +@ShouldThrow( + 'The classes `SuperSuperSuperWithConflictingNestedDiscriminator` and ' + '`SuperWithConflictingNestedDiscriminator` are nested sealed classes, but they have ' + 'the same discriminator `this_will_conflict`.', + todo: + 'Rename one of the discriminators with `unionDiscriminator` ' + 'field of `@JsonSerializable`.', + element: 'SuperWithConflictingNestedDiscriminator', +) +@JsonSerializable(unionDiscriminator: 'this_will_conflict') +sealed class SuperWithConflictingNestedDiscriminator + extends SuperSuperWithConflictingNestedDiscriminator {} diff --git a/json_serializable/test/src/generic_test_input.dart b/json_serializable/test/src/generic_test_input.dart index 810c5a67..aa8be42b 100644 --- a/json_serializable/test/src/generic_test_input.dart +++ b/json_serializable/test/src/generic_test_input.dart @@ -85,3 +85,82 @@ Map _$GenericArgumentFactoriesFlagWithoutGenericTypeToJson( ) @JsonSerializable(genericArgumentFactories: true) class GenericArgumentFactoriesFlagWithoutGenericType {} + +@ShouldThrow( + 'The class `SuperWithGenericArgumentFactories` is annotated ' + 'with `JsonSerializable` field `genericArgumentFactories: true`. ' + '`genericArgumentFactories: true` is not supported for classes ' + 'that are sealed or have sealed superclasses.', + todo: 'Remove the `genericArgumentFactories` option or ' + 'remove the `sealed` keyword from the class.', + element: 'SuperWithGenericArgumentFactories', +) +@JsonSerializable( + genericArgumentFactories: true, +) +sealed class SuperWithGenericArgumentFactories {} + +@JsonSerializable( + genericArgumentFactories: false, +) +sealed class SuperWithoutGenericArgumentFactories {} + +@ShouldThrow( + 'The class `SubWithSubGenericArgumentFactoriesExt` is annotated ' + 'with `JsonSerializable` field `genericArgumentFactories: true`. ' + '`genericArgumentFactories: true` is not supported for classes ' + 'that are sealed or have sealed superclasses.', + todo: 'Remove the `genericArgumentFactories` option or ' + 'remove the `sealed` keyword from the class.', + element: 'SubWithSubGenericArgumentFactoriesExt', +) +@JsonSerializable( + genericArgumentFactories: true, +) +class SubWithSubGenericArgumentFactoriesExt + extends SuperWithoutGenericArgumentFactories {} + +@ShouldThrow( + 'The class `SubWithSubGenericArgumentFactoriesImpl` is annotated ' + 'with `JsonSerializable` field `genericArgumentFactories: true`. ' + '`genericArgumentFactories: true` is not supported for classes ' + 'that are sealed or have sealed superclasses.', + todo: 'Remove the `genericArgumentFactories` option or ' + 'remove the `sealed` keyword from the class.', + element: 'SubWithSubGenericArgumentFactoriesImpl', +) +@JsonSerializable( + genericArgumentFactories: true, +) +class SubWithSubGenericArgumentFactoriesImpl + implements SuperWithoutGenericArgumentFactories {} + +@ShouldThrow( + 'The class `SubWithSubAndSuperGenericArgumentFactoriesExt` is annotated ' + 'with `JsonSerializable` field `genericArgumentFactories: true`. ' + '`genericArgumentFactories: true` is not supported for classes ' + 'that are sealed or have sealed superclasses.', + todo: 'Remove the `genericArgumentFactories` option or ' + 'remove the `sealed` keyword from the class.', + element: 'SubWithSubAndSuperGenericArgumentFactoriesExt', +) +@JsonSerializable( + genericArgumentFactories: true, +) +class SubWithSubAndSuperGenericArgumentFactoriesExt + extends SuperWithGenericArgumentFactories {} + +@ShouldThrow( + 'The class `SubWithSubAndSuperGenericArgumentFactoriesImpl` is annotated ' + 'with `JsonSerializable` field `genericArgumentFactories: true`. ' + '`genericArgumentFactories: true` is not supported for classes ' + 'that are sealed or have sealed superclasses.', + todo: 'Remove the `genericArgumentFactories` option or ' + 'remove the `sealed` keyword from the class.', + element: 'SubWithSubAndSuperGenericArgumentFactoriesImpl', +) +@JsonSerializable( + genericArgumentFactories: true, +) +class SubWithSubAndSuperGenericArgumentFactoriesImpl + implements SuperWithGenericArgumentFactories {} diff --git a/json_serializable/test/src/mismatching_config_input.dart b/json_serializable/test/src/mismatching_config_input.dart new file mode 100644 index 00000000..1c9e7598 --- /dev/null +++ b/json_serializable/test/src/mismatching_config_input.dart @@ -0,0 +1,45 @@ +// @dart=3.8 + +part of '_json_serializable_test_input.dart'; + +@JsonSerializable(createToJson: false) +sealed class SuperWithoutToJson {} + +@JsonSerializable(createToJson: true) +sealed class SuperWithToJson {} + +@ShouldThrow( + 'The class `SuperWithoutToJson` is sealed but its ' + 'subclass `SubWithToJsonAndSuperWithoutToJsonExt` has a different ' + '`createToJson` option than the base class.', + element: 'SubWithToJsonAndSuperWithoutToJsonExt', +) +@JsonSerializable(createToJson: true) +class SubWithToJsonAndSuperWithoutToJsonExt extends SuperWithoutToJson {} + +@ShouldThrow( + 'The class `SuperWithoutToJson` is sealed but its ' + 'subclass `SubWithToJsonAndSuperWithoutToJsonImpl` has a different ' + '`createToJson` option than the base class.', + element: 'SubWithToJsonAndSuperWithoutToJsonImpl', +) +@JsonSerializable(createToJson: true) +class SubWithToJsonAndSuperWithoutToJsonImpl implements SuperWithoutToJson {} + +@ShouldThrow( + 'The class `SuperWithToJson` is sealed but its ' + 'subclass `SubWithoutToJsonAndSuperWithToJsonExt` has a different ' + '`createToJson` option than the base class.', + element: 'SubWithoutToJsonAndSuperWithToJsonExt', +) +@JsonSerializable(createToJson: false) +class SubWithoutToJsonAndSuperWithToJsonExt extends SuperWithToJson {} + +@ShouldThrow( + 'The class `SuperWithToJson` is sealed but its ' + 'subclass `SubWithoutToJsonAndSuperWithToJsonImpl` has a different ' + '`createToJson` option than the base class.', + element: 'SubWithoutToJsonAndSuperWithToJsonImpl', +) +@JsonSerializable(createToJson: false) +class SubWithoutToJsonAndSuperWithToJsonImpl implements SuperWithToJson {} diff --git a/json_serializable/test/src/missing_annotation_input.dart b/json_serializable/test/src/missing_annotation_input.dart new file mode 100644 index 00000000..d648e74c --- /dev/null +++ b/json_serializable/test/src/missing_annotation_input.dart @@ -0,0 +1,56 @@ +// @dart=3.8 + +part of '_json_serializable_test_input.dart'; + +sealed class SuperWithoutSuperJsonSerializableAnnotation {} + +@ShouldThrow( + 'The class `SubWithoutSuperJsonSerializableAnnotationExt` is annotated ' + 'with `JsonSerializable` but its sealed superclass ' + '`SuperWithoutSuperJsonSerializableAnnotation` is not annotated ' + 'with `JsonSerializable`.', + element: 'SubWithoutSuperJsonSerializableAnnotationExt', +) +@JsonSerializable() +class SubWithoutSuperJsonSerializableAnnotationExt + extends SuperWithoutSuperJsonSerializableAnnotation {} + +@ShouldThrow( + 'The class `SubWithoutSuperJsonSerializableAnnotationImpl` is annotated ' + 'with `JsonSerializable` but its sealed superclass ' + '`SuperWithoutSuperJsonSerializableAnnotation` is not annotated ' + 'with `JsonSerializable`.', + todo: 'Add `@JsonSerializable` annotation to the sealed class.', + element: 'SubWithoutSuperJsonSerializableAnnotationImpl', +) +@JsonSerializable() +class SubWithoutSuperJsonSerializableAnnotationImpl + implements SuperWithoutSuperJsonSerializableAnnotation {} + +@ShouldThrow( + 'The class `SuperWithSubExtWithoutJsonSerializableAnnotation` is sealed but its ' + 'subclass `SubWithoutJsonSerializableAnnotationExt` is not annotated with ' + '`JsonSerializable`.', + todo: + 'Add `@JsonSerializable` annotation to SubWithoutJsonSerializableAnnotationExt.', + element: 'SubWithoutJsonSerializableAnnotationExt', +) +@JsonSerializable() +sealed class SuperWithSubExtWithoutJsonSerializableAnnotation {} + +class SubWithoutJsonSerializableAnnotationExt + extends SuperWithSubExtWithoutJsonSerializableAnnotation {} + +@ShouldThrow( + 'The class `SuperWithSubImplWithoutJsonSerializableAnnotation` is sealed but its ' + 'subclass `SubWithoutJsonSerializableAnnotationImpl` is not annotated with ' + '`JsonSerializable`.', + todo: + 'Add `@JsonSerializable` annotation to SubWithoutJsonSerializableAnnotationImpl.', + element: 'SubWithoutJsonSerializableAnnotationImpl', +) +@JsonSerializable() +sealed class SuperWithSubImplWithoutJsonSerializableAnnotation {} + +class SubWithoutJsonSerializableAnnotationImpl + implements SuperWithSubImplWithoutJsonSerializableAnnotation {} diff --git a/json_serializable/test/src/sealed_test_input.dart b/json_serializable/test/src/sealed_test_input.dart new file mode 100644 index 00000000..cc184c44 --- /dev/null +++ b/json_serializable/test/src/sealed_test_input.dart @@ -0,0 +1,779 @@ +// @dart=3.8 + +part of '_json_serializable_test_input.dart'; + +@ShouldGenerate(r''' +SuperSimpleSealedClass _$SuperSimpleSealedClassFromJson( + Map json) => + switch (json['type']) { + 'SubOneSimpleSealedClass' => _$SubOneSimpleSealedClassFromJson(json), + 'SubTwoSimpleSealedClass' => _$SubTwoSimpleSealedClassFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperSimpleSealedClass, + json, + ), + }; + +Map _$SuperSimpleSealedClassToJson( + SuperSimpleSealedClass instance) => + switch (instance) { + final SubOneSimpleSealedClass instance => { + 'type': 'SubOneSimpleSealedClass', + ..._$SubOneSimpleSealedClassToJson(instance), + }, + final SubTwoSimpleSealedClass instance => { + 'type': 'SubTwoSimpleSealedClass', + ..._$SubTwoSimpleSealedClassToJson(instance), + }, + }; +''') +@JsonSerializable() +sealed class SuperSimpleSealedClass { + const SuperSimpleSealedClass(); +} + +@ShouldGenerate(r''' +SubOneSimpleSealedClass _$SubOneSimpleSealedClassFromJson( + Map json) => + SubOneSimpleSealedClass( + someField: json['someField'] as String, + ); + +Map _$SubOneSimpleSealedClassToJson( + SubOneSimpleSealedClass instance) => + { + 'someField': instance.someField, + }; +''') +@JsonSerializable() +class SubOneSimpleSealedClass extends SuperSimpleSealedClass { + final String someField; + + SubOneSimpleSealedClass({required this.someField}); +} + +@ShouldGenerate(r''' +SubTwoSimpleSealedClass _$SubTwoSimpleSealedClassFromJson( + Map json) => + SubTwoSimpleSealedClass( + someField: json['someField'] as String, + ); + +Map _$SubTwoSimpleSealedClassToJson( + SubTwoSimpleSealedClass instance) => + { + 'someField': instance.someField, + }; +''') +@JsonSerializable() +class SubTwoSimpleSealedClass extends SuperSimpleSealedClass { + final String someField; + + SubTwoSimpleSealedClass({required this.someField}); +} + +@ShouldGenerate(r''' +SuperSimpleSealedClassWithChangedDiscriminator + _$SuperSimpleSealedClassWithChangedDiscriminatorFromJson( + Map json) => + switch (json['new_discriminator']) { + 'SubOneSimpleSealedClassWithChangedDiscriminator' => + _$SubOneSimpleSealedClassWithChangedDiscriminatorFromJson(json), + 'SubTwoSimpleSealedClassWithChangedDiscriminator' => + _$SubTwoSimpleSealedClassWithChangedDiscriminatorFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['new_discriminator']}', + SuperSimpleSealedClassWithChangedDiscriminator, + json, + ), + }; + +Map _$SuperSimpleSealedClassWithChangedDiscriminatorToJson( + SuperSimpleSealedClassWithChangedDiscriminator instance) => + switch (instance) { + final SubOneSimpleSealedClassWithChangedDiscriminator instance => { + 'new_discriminator': + 'SubOneSimpleSealedClassWithChangedDiscriminator', + ..._$SubOneSimpleSealedClassWithChangedDiscriminatorToJson(instance), + }, + final SubTwoSimpleSealedClassWithChangedDiscriminator instance => { + 'new_discriminator': + 'SubTwoSimpleSealedClassWithChangedDiscriminator', + ..._$SubTwoSimpleSealedClassWithChangedDiscriminatorToJson(instance), + }, + }; +''') +@JsonSerializable(unionDiscriminator: 'new_discriminator') +sealed class SuperSimpleSealedClassWithChangedDiscriminator { + const SuperSimpleSealedClassWithChangedDiscriminator(); +} + +@ShouldGenerate(r''' +SubOneSimpleSealedClassWithChangedDiscriminator + _$SubOneSimpleSealedClassWithChangedDiscriminatorFromJson( + Map json) => + SubOneSimpleSealedClassWithChangedDiscriminator( + someField: json['someField'] as String, + ); + +Map _$SubOneSimpleSealedClassWithChangedDiscriminatorToJson( + SubOneSimpleSealedClassWithChangedDiscriminator instance) => + { + 'someField': instance.someField, + }; +''') +@JsonSerializable() +class SubOneSimpleSealedClassWithChangedDiscriminator + extends SuperSimpleSealedClassWithChangedDiscriminator { + final String someField; + + SubOneSimpleSealedClassWithChangedDiscriminator({required this.someField}); +} + +@ShouldGenerate(r''' +SubTwoSimpleSealedClassWithChangedDiscriminator + _$SubTwoSimpleSealedClassWithChangedDiscriminatorFromJson( + Map json) => + SubTwoSimpleSealedClassWithChangedDiscriminator( + someField: json['someField'] as String, + ); + +Map _$SubTwoSimpleSealedClassWithChangedDiscriminatorToJson( + SubTwoSimpleSealedClassWithChangedDiscriminator instance) => + { + 'someField': instance.someField, + }; +''') +@JsonSerializable() +class SubTwoSimpleSealedClassWithChangedDiscriminator + extends SuperSimpleSealedClassWithChangedDiscriminator { + final String someField; + + SubTwoSimpleSealedClassWithChangedDiscriminator({required this.someField}); +} + +@ShouldGenerate(r''' +SuperSimpleSealedClassWithChangedUnionRename + _$SuperSimpleSealedClassWithChangedUnionRenameFromJson( + Map json) => + switch (json['type']) { + 'sub_one_simple_sealed_class_with_changed_union_rename' => + _$SubOneSimpleSealedClassWithChangedUnionRenameFromJson(json), + 'sub_two_simple_sealed_class_with_changed_union_rename' => + _$SubTwoSimpleSealedClassWithChangedUnionRenameFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperSimpleSealedClassWithChangedUnionRename, + json, + ), + }; + +Map _$SuperSimpleSealedClassWithChangedUnionRenameToJson( + SuperSimpleSealedClassWithChangedUnionRename instance) => + switch (instance) { + final SubOneSimpleSealedClassWithChangedUnionRename instance => { + 'type': 'sub_one_simple_sealed_class_with_changed_union_rename', + ..._$SubOneSimpleSealedClassWithChangedUnionRenameToJson(instance), + }, + final SubTwoSimpleSealedClassWithChangedUnionRename instance => { + 'type': 'sub_two_simple_sealed_class_with_changed_union_rename', + ..._$SubTwoSimpleSealedClassWithChangedUnionRenameToJson(instance), + }, + }; +''') +@JsonSerializable(unionRename: UnionRename.snake) +sealed class SuperSimpleSealedClassWithChangedUnionRename { + const SuperSimpleSealedClassWithChangedUnionRename(); +} + +@ShouldGenerate(r''' +SubOneSimpleSealedClassWithChangedUnionRename + _$SubOneSimpleSealedClassWithChangedUnionRenameFromJson( + Map json) => + SubOneSimpleSealedClassWithChangedUnionRename( + someField: json['someField'] as String, + ); + +Map _$SubOneSimpleSealedClassWithChangedUnionRenameToJson( + SubOneSimpleSealedClassWithChangedUnionRename instance) => + { + 'someField': instance.someField, + }; +''') +@JsonSerializable() +class SubOneSimpleSealedClassWithChangedUnionRename + extends SuperSimpleSealedClassWithChangedUnionRename { + final String someField; + + SubOneSimpleSealedClassWithChangedUnionRename({required this.someField}); +} + +@ShouldGenerate(r''' +SubTwoSimpleSealedClassWithChangedUnionRename + _$SubTwoSimpleSealedClassWithChangedUnionRenameFromJson( + Map json) => + SubTwoSimpleSealedClassWithChangedUnionRename( + someField: json['someField'] as String, + ); + +Map _$SubTwoSimpleSealedClassWithChangedUnionRenameToJson( + SubTwoSimpleSealedClassWithChangedUnionRename instance) => + { + 'someField': instance.someField, + }; +''') +@JsonSerializable() +class SubTwoSimpleSealedClassWithChangedUnionRename + extends SuperSimpleSealedClassWithChangedUnionRename { + final String someField; + + SubTwoSimpleSealedClassWithChangedUnionRename({required this.someField}); +} + +@ShouldGenerate(r''' +SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename + _$SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + Map json) => + switch (json['my_discriminator']) { + 'sub-one-simple-sealed-class-with-changed-discriminator-and-union-rename' => + _$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + json), + 'sub-two-simple-sealed-class-with-changed-discriminator-and-union-rename' => + _$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + json), + _ => throw UnrecognizedUnionTypeException( + '${json['my_discriminator']}', + SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename, + json, + ), + }; + +Map + _$SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename + instance) => + switch (instance) { + final SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename + instance => + { + 'my_discriminator': + 'sub-one-simple-sealed-class-with-changed-discriminator-and-union-rename', + ..._$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + instance), + }, + final SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename + instance => + { + 'my_discriminator': + 'sub-two-simple-sealed-class-with-changed-discriminator-and-union-rename', + ..._$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + instance), + }, + }; +''') +@JsonSerializable( + unionDiscriminator: 'my_discriminator', + unionRename: UnionRename.kebab, +) +sealed class SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename { + const SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename(); +} + +@ShouldGenerate(r''' +SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename + _$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + Map json) => + SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename( + someField: json['someField'] as String, + ); + +Map + _$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename + instance) => + { + 'someField': instance.someField, + }; +''') +@JsonSerializable() +class SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename + extends SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename { + final String someField; + + SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename({ + required this.someField, + }); +} + +@ShouldGenerate(r''' +SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename + _$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + Map json) => + SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename( + someField: json['someField'] as String, + ); + +Map + _$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename + instance) => + { + 'someField': instance.someField, + }; +''') +@JsonSerializable() +class SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename + extends SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename { + final String someField; + + SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename({ + required this.someField, + }); +} + +@ShouldGenerate(r''' +SuperSimpleSealedClassWithoutToJson + _$SuperSimpleSealedClassWithoutToJsonFromJson(Map json) => + switch (json['type']) { + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperSimpleSealedClassWithoutToJson, + json, + ), + }; +''') +@JsonSerializable(createToJson: false) +sealed class SuperSimpleSealedClassWithoutToJson {} + +@ShouldGenerate(r''' +SubSimpleSealedClassWithoutToJson _$SubSimpleSealedClassWithoutToJsonFromJson( + Map json) => + SubSimpleSealedClassWithoutToJson( + someField: json['someField'] as String, + ); +''') +@JsonSerializable(createToJson: false) +class SubSimpleSealedClassWithoutToJson { + final String someField; + + SubSimpleSealedClassWithoutToJson({required this.someField}); +} + +@ShouldGenerate(r''' +SuperSuperSuperNested _$SuperSuperSuperNestedFromJson( + Map json) => + switch (json['super_super_super_type']) { + 'SuperSuperNestedOne' => _$SuperSuperNestedOneFromJson(json), + 'SuperSuperNestedTwo' => _$SuperSuperNestedTwoFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['super_super_super_type']}', + SuperSuperSuperNested, + json, + ), + }; + +Map _$SuperSuperSuperNestedToJson( + SuperSuperSuperNested instance) => + switch (instance) { + final SuperSuperNestedOne instance => { + 'super_super_super_type': 'SuperSuperNestedOne', + ..._$SuperSuperNestedOneToJson(instance), + }, + final SuperSuperNestedTwo instance => { + 'super_super_super_type': 'SuperSuperNestedTwo', + ..._$SuperSuperNestedTwoToJson(instance), + }, + }; +''') +@JsonSerializable(unionDiscriminator: 'super_super_super_type') +sealed class SuperSuperSuperNested { + const SuperSuperSuperNested(); +} + +@ShouldGenerate(r''' +SuperSuperNestedOne _$SuperSuperNestedOneFromJson(Map json) => + switch (json['super_super_type']) { + 'SuperNestedOneOne' => _$SuperNestedOneOneFromJson(json), + 'SuperNestedOneTwo' => _$SuperNestedOneTwoFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['super_super_type']}', + SuperSuperNestedOne, + json, + ), + }; + +Map _$SuperSuperNestedOneToJson( + SuperSuperNestedOne instance) => + switch (instance) { + final SuperNestedOneOne instance => { + 'super_super_type': 'SuperNestedOneOne', + ..._$SuperNestedOneOneToJson(instance), + }, + final SuperNestedOneTwo instance => { + 'super_super_type': 'SuperNestedOneTwo', + ..._$SuperNestedOneTwoToJson(instance), + }, + }; +''') +@JsonSerializable(unionDiscriminator: 'super_super_type') +sealed class SuperSuperNestedOne extends SuperSuperSuperNested { + const SuperSuperNestedOne(); +} + +@ShouldGenerate(r''' +SuperSuperNestedTwo _$SuperSuperNestedTwoFromJson(Map json) => + switch (json['super_super_type']) { + 'SuperNestedTwoOne' => _$SuperNestedTwoOneFromJson(json), + 'SuperNestedTwoTwo' => _$SuperNestedTwoTwoFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['super_super_type']}', + SuperSuperNestedTwo, + json, + ), + }; + +Map _$SuperSuperNestedTwoToJson( + SuperSuperNestedTwo instance) => + switch (instance) { + final SuperNestedTwoOne instance => { + 'super_super_type': 'SuperNestedTwoOne', + ..._$SuperNestedTwoOneToJson(instance), + }, + final SuperNestedTwoTwo instance => { + 'super_super_type': 'SuperNestedTwoTwo', + ..._$SuperNestedTwoTwoToJson(instance), + }, + }; +''') +@JsonSerializable(unionDiscriminator: 'super_super_type') +sealed class SuperSuperNestedTwo extends SuperSuperSuperNested { + const SuperSuperNestedTwo(); +} + +@ShouldGenerate(r''' +SuperNestedOneOne _$SuperNestedOneOneFromJson(Map json) => + switch (json['super_type']) { + 'SubNestedOneOne' => _$SubNestedOneOneFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['super_type']}', + SuperNestedOneOne, + json, + ), + }; + +Map _$SuperNestedOneOneToJson(SuperNestedOneOne instance) => + switch (instance) { + final SubNestedOneOne instance => { + 'super_type': 'SubNestedOneOne', + ..._$SubNestedOneOneToJson(instance), + }, + }; +''') +@JsonSerializable(unionDiscriminator: 'super_type') +sealed class SuperNestedOneOne extends SuperSuperNestedOne { + const SuperNestedOneOne(); +} + +@ShouldGenerate(r''' +SuperNestedOneTwo _$SuperNestedOneTwoFromJson(Map json) => + switch (json['super_type']) { + 'SubNestedOneTwo' => _$SubNestedOneTwoFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['super_type']}', + SuperNestedOneTwo, + json, + ), + }; + +Map _$SuperNestedOneTwoToJson(SuperNestedOneTwo instance) => + switch (instance) { + final SubNestedOneTwo instance => { + 'super_type': 'SubNestedOneTwo', + ..._$SubNestedOneTwoToJson(instance), + }, + }; +''') +@JsonSerializable(unionDiscriminator: 'super_type') +sealed class SuperNestedOneTwo extends SuperSuperNestedOne { + const SuperNestedOneTwo(); +} + +@ShouldGenerate(r''' +SuperNestedTwoOne _$SuperNestedTwoOneFromJson(Map json) => + switch (json['super_type']) { + 'SubNestedTwoOne' => _$SubNestedTwoOneFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['super_type']}', + SuperNestedTwoOne, + json, + ), + }; + +Map _$SuperNestedTwoOneToJson(SuperNestedTwoOne instance) => + switch (instance) { + final SubNestedTwoOne instance => { + 'super_type': 'SubNestedTwoOne', + ..._$SubNestedTwoOneToJson(instance), + }, + }; +''') +@JsonSerializable(unionDiscriminator: 'super_type') +sealed class SuperNestedTwoOne extends SuperSuperNestedTwo { + const SuperNestedTwoOne(); +} + +@ShouldGenerate(r''' +SuperNestedTwoTwo _$SuperNestedTwoTwoFromJson(Map json) => + switch (json['super_type']) { + 'SubNestedTwoTwo' => _$SubNestedTwoTwoFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['super_type']}', + SuperNestedTwoTwo, + json, + ), + }; + +Map _$SuperNestedTwoTwoToJson(SuperNestedTwoTwo instance) => + switch (instance) { + final SubNestedTwoTwo instance => { + 'super_type': 'SubNestedTwoTwo', + ..._$SubNestedTwoTwoToJson(instance), + }, + }; +''') +@JsonSerializable(unionDiscriminator: 'super_type') +sealed class SuperNestedTwoTwo extends SuperSuperNestedTwo { + const SuperNestedTwoTwo(); +} + +@ShouldGenerate(r''' +SubNestedOneOne _$SubNestedOneOneFromJson(Map json) => + SubNestedOneOne( + oneOneField: json['oneOneField'] as String, + ); + +Map _$SubNestedOneOneToJson(SubNestedOneOne instance) => + { + 'oneOneField': instance.oneOneField, + }; +''') +@JsonSerializable() +class SubNestedOneOne extends SuperNestedOneOne { + final String oneOneField; + + SubNestedOneOne({required this.oneOneField}); +} + +@ShouldGenerate(r''' +SubNestedOneTwo _$SubNestedOneTwoFromJson(Map json) => + SubNestedOneTwo( + oneTwoField: json['oneTwoField'] as String, + ); + +Map _$SubNestedOneTwoToJson(SubNestedOneTwo instance) => + { + 'oneTwoField': instance.oneTwoField, + }; +''') +@JsonSerializable() +class SubNestedOneTwo extends SuperNestedOneTwo { + final String oneTwoField; + + SubNestedOneTwo({required this.oneTwoField}); +} + +@ShouldGenerate(r''' +SubNestedTwoOne _$SubNestedTwoOneFromJson(Map json) => + SubNestedTwoOne( + twoOneField: json['twoOneField'] as String, + ); + +Map _$SubNestedTwoOneToJson(SubNestedTwoOne instance) => + { + 'twoOneField': instance.twoOneField, + }; +''') +@JsonSerializable() +class SubNestedTwoOne extends SuperNestedTwoOne { + final String twoOneField; + + SubNestedTwoOne({required this.twoOneField}); +} + +@ShouldGenerate(r''' +SubNestedTwoTwo _$SubNestedTwoTwoFromJson(Map json) => + SubNestedTwoTwo( + twoTwoField: json['twoTwoField'] as String, + ); + +Map _$SubNestedTwoTwoToJson(SubNestedTwoTwo instance) => + { + 'twoTwoField': instance.twoTwoField, + }; +''') +@JsonSerializable() +class SubNestedTwoTwo extends SuperNestedTwoTwo { + final String twoTwoField; + + SubNestedTwoTwo({required this.twoTwoField}); +} + +@ShouldGenerate(r''' +SuperMultipleImplOne _$SuperMultipleImplOneFromJson( + Map json) => + switch (json['type']) { + 'SubOneMultipleImpl' => _$SubOneMultipleImplFromJson(json), + 'SubThreeMultipleImpl' => _$SubThreeMultipleImplFromJson(json), + 'SubFourMultipleImpl' => _$SubFourMultipleImplFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperMultipleImplOne, + json, + ), + }; + +Map _$SuperMultipleImplOneToJson( + SuperMultipleImplOne instance) => + switch (instance) { + final SubOneMultipleImpl instance => { + 'type': 'SubOneMultipleImpl', + ..._$SubOneMultipleImplToJson(instance), + }, + final SubThreeMultipleImpl instance => { + 'type': 'SubThreeMultipleImpl', + ..._$SubThreeMultipleImplToJson(instance), + }, + final SubFourMultipleImpl instance => { + 'type': 'SubFourMultipleImpl', + ..._$SubFourMultipleImplToJson(instance), + }, + }; +''') +@JsonSerializable() +sealed class SuperMultipleImplOne {} + +@ShouldGenerate(r''' +SuperMultipleImplTwo _$SuperMultipleImplTwoFromJson( + Map json) => + switch (json['type']) { + 'SubTwoMultipleImpl' => _$SubTwoMultipleImplFromJson(json), + 'SubThreeMultipleImpl' => _$SubThreeMultipleImplFromJson(json), + 'SubFourMultipleImpl' => _$SubFourMultipleImplFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperMultipleImplTwo, + json, + ), + }; + +Map _$SuperMultipleImplTwoToJson( + SuperMultipleImplTwo instance) => + switch (instance) { + final SubTwoMultipleImpl instance => { + 'type': 'SubTwoMultipleImpl', + ..._$SubTwoMultipleImplToJson(instance), + }, + final SubThreeMultipleImpl instance => { + 'type': 'SubThreeMultipleImpl', + ..._$SubThreeMultipleImplToJson(instance), + }, + final SubFourMultipleImpl instance => { + 'type': 'SubFourMultipleImpl', + ..._$SubFourMultipleImplToJson(instance), + }, + }; +''') +@JsonSerializable() +sealed class SuperMultipleImplTwo {} + +@ShouldGenerate(r''' +SubOneMultipleImpl _$SubOneMultipleImplFromJson(Map json) => + SubOneMultipleImpl( + json['subOneField'] as String, + ); + +Map _$SubOneMultipleImplToJson(SubOneMultipleImpl instance) => + { + 'subOneField': instance.subOneField, + }; +''') +@JsonSerializable() +class SubOneMultipleImpl implements SuperMultipleImplOne { + final String subOneField; + + SubOneMultipleImpl(this.subOneField); +} + +@ShouldGenerate(r''' +SubTwoMultipleImpl _$SubTwoMultipleImplFromJson(Map json) => + SubTwoMultipleImpl( + json['subTwoField'] as String, + ); + +Map _$SubTwoMultipleImplToJson(SubTwoMultipleImpl instance) => + { + 'subTwoField': instance.subTwoField, + }; +''') +@JsonSerializable() +class SubTwoMultipleImpl implements SuperMultipleImplTwo { + final String subTwoField; + + SubTwoMultipleImpl(this.subTwoField); +} + +@ShouldGenerate(r''' +SubThreeMultipleImpl _$SubThreeMultipleImplFromJson( + Map json) => + SubThreeMultipleImpl( + json['subThreeField'] as String, + ); + +Map _$SubThreeMultipleImplToJson( + SubThreeMultipleImpl instance) => + { + 'subThreeField': instance.subThreeField, + }; +''') +@JsonSerializable() +class SubThreeMultipleImpl + implements SuperMultipleImplOne, SuperMultipleImplTwo { + final String subThreeField; + + SubThreeMultipleImpl(this.subThreeField); +} + +@ShouldGenerate(r''' +SubFourMultipleImpl _$SubFourMultipleImplFromJson(Map json) => + SubFourMultipleImpl( + json['subFourField'] as String, + ); + +Map _$SubFourMultipleImplToJson( + SubFourMultipleImpl instance) => + { + 'subFourField': instance.subFourField, + }; +''') +@JsonSerializable() +class SubFourMultipleImpl + implements SuperMultipleImplOne, SuperMultipleImplTwo { + final String subFourField; + + SubFourMultipleImpl(this.subFourField); +} + + + + +/// +/// +/// Happy cases: +/// - Simple with two subclasses +/// - Simple with changed discriminator +/// - Simple with union rename +/// - Simple with changed discriminator and union rename +/// - Complex nested +/// - Complex many implementations +/// diff --git a/json_serializable/test/src/union_namer_input.dart b/json_serializable/test/src/union_namer_input.dart new file mode 100644 index 00000000..a1225e27 --- /dev/null +++ b/json_serializable/test/src/union_namer_input.dart @@ -0,0 +1,180 @@ +// @dart=3.8 + +part of '_json_serializable_test_input.dart'; + +@ShouldGenerate(r''' +SuperUnionRenameNone _$SuperUnionRenameNoneFromJson( + Map json) => + switch (json['type']) { + 'SubUnionRenameNone' => _$SubUnionRenameNoneFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenameNone, + json, + ), + }; + +Map _$SuperUnionRenameNoneToJson( + SuperUnionRenameNone instance) => + switch (instance) { + final SubUnionRenameNone instance => { + 'type': 'SubUnionRenameNone', + ..._$SubUnionRenameNoneToJson(instance), + }, + }; +''') +@JsonSerializable(unionRename: UnionRename.none) +sealed class SuperUnionRenameNone {} + +@ShouldGenerate(r''' +SubUnionRenameNone _$SubUnionRenameNoneFromJson(Map json) => + SubUnionRenameNone(); + +Map _$SubUnionRenameNoneToJson(SubUnionRenameNone instance) => + {}; +''') +@JsonSerializable() +class SubUnionRenameNone extends SuperUnionRenameNone {} + +@ShouldGenerate(r''' +SuperUnionRenameKebab _$SuperUnionRenameKebabFromJson( + Map json) => + switch (json['type']) { + 'sub-union-rename-kebab' => _$SubUnionRenameKebabFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenameKebab, + json, + ), + }; + +Map _$SuperUnionRenameKebabToJson( + SuperUnionRenameKebab instance) => + switch (instance) { + final SubUnionRenameKebab instance => { + 'type': 'sub-union-rename-kebab', + ..._$SubUnionRenameKebabToJson(instance), + }, + }; +''') +@JsonSerializable(unionRename: UnionRename.kebab) +sealed class SuperUnionRenameKebab {} + +@ShouldGenerate(r''' +SubUnionRenameKebab _$SubUnionRenameKebabFromJson(Map json) => + SubUnionRenameKebab(); + +Map _$SubUnionRenameKebabToJson( + SubUnionRenameKebab instance) => + {}; +''') +@JsonSerializable() +class SubUnionRenameKebab extends SuperUnionRenameKebab {} + +@ShouldGenerate(r''' +SuperUnionRenameSnake _$SuperUnionRenameSnakeFromJson( + Map json) => + switch (json['type']) { + 'sub_union_rename_snake' => _$SubUnionRenameSnakeFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenameSnake, + json, + ), + }; + +Map _$SuperUnionRenameSnakeToJson( + SuperUnionRenameSnake instance) => + switch (instance) { + final SubUnionRenameSnake instance => { + 'type': 'sub_union_rename_snake', + ..._$SubUnionRenameSnakeToJson(instance), + }, + }; +''') +@JsonSerializable(unionRename: UnionRename.snake) +sealed class SuperUnionRenameSnake {} + +@ShouldGenerate(r''' +SubUnionRenameSnake _$SubUnionRenameSnakeFromJson(Map json) => + SubUnionRenameSnake(); + +Map _$SubUnionRenameSnakeToJson( + SubUnionRenameSnake instance) => + {}; +''') +@JsonSerializable() +class SubUnionRenameSnake extends SuperUnionRenameSnake {} + +@ShouldGenerate(r''' +SuperUnionRenamePascal _$SuperUnionRenamePascalFromJson( + Map json) => + switch (json['type']) { + 'SubUnionRenamePascal' => _$SubUnionRenamePascalFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenamePascal, + json, + ), + }; + +Map _$SuperUnionRenamePascalToJson( + SuperUnionRenamePascal instance) => + switch (instance) { + final SubUnionRenamePascal instance => { + 'type': 'SubUnionRenamePascal', + ..._$SubUnionRenamePascalToJson(instance), + }, + }; +''') +@JsonSerializable(unionRename: UnionRename.pascal) +sealed class SuperUnionRenamePascal {} + +@ShouldGenerate(r''' +SubUnionRenamePascal _$SubUnionRenamePascalFromJson( + Map json) => + SubUnionRenamePascal(); + +Map _$SubUnionRenamePascalToJson( + SubUnionRenamePascal instance) => + {}; +''') +@JsonSerializable() +class SubUnionRenamePascal extends SuperUnionRenamePascal {} + +@ShouldGenerate(r''' +SuperUnionRenameScreamingSnake _$SuperUnionRenameScreamingSnakeFromJson( + Map json) => + switch (json['type']) { + 'SUB_UNION_RENAME_SCREAMING_SNAKE' => + _$SubUnionRenameScreamingSnakeFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenameScreamingSnake, + json, + ), + }; + +Map _$SuperUnionRenameScreamingSnakeToJson( + SuperUnionRenameScreamingSnake instance) => + switch (instance) { + final SubUnionRenameScreamingSnake instance => { + 'type': 'SUB_UNION_RENAME_SCREAMING_SNAKE', + ..._$SubUnionRenameScreamingSnakeToJson(instance), + }, + }; +''') +@JsonSerializable(unionRename: UnionRename.screamingSnake) +sealed class SuperUnionRenameScreamingSnake {} + +@ShouldGenerate(r''' +SubUnionRenameScreamingSnake _$SubUnionRenameScreamingSnakeFromJson( + Map json) => + SubUnionRenameScreamingSnake(); + +Map _$SubUnionRenameScreamingSnakeToJson( + SubUnionRenameScreamingSnake instance) => + {}; +''') +@JsonSerializable() +class SubUnionRenameScreamingSnake extends SuperUnionRenameScreamingSnake {} From c77bf7cba7369514e00b8db0061fecb222585665 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Sun, 6 Apr 2025 21:07:37 +0300 Subject: [PATCH 09/18] test: add integration tests --- .../test/integration/integration_test.dart | 53 ++++++++ .../integration/sealed_class_examples.dart | 115 +++++++++++++++++ .../integration/sealed_class_examples.g.dart | 117 ++++++++++++++++++ .../test/src/sealed_test_input.dart | 14 --- 4 files changed, 285 insertions(+), 14 deletions(-) create mode 100644 json_serializable/test/integration/sealed_class_examples.dart create mode 100644 json_serializable/test/integration/sealed_class_examples.g.dart diff --git a/json_serializable/test/integration/integration_test.dart b/json_serializable/test/integration/integration_test.dart index 9cc5d094..d0ce8d41 100644 --- a/json_serializable/test/integration/integration_test.dart +++ b/json_serializable/test/integration/integration_test.dart @@ -13,6 +13,7 @@ import 'json_enum_example.dart'; import 'json_keys_example.dart' as js_keys; import 'json_test_common.dart' show Category, Platform, StatusCode; import 'json_test_example.dart'; +import 'sealed_class_examples.dart'; Matcher _throwsArgumentError(Object matcher) => throwsA(isArgumentError.having((e) => e.message, 'message', matcher)); @@ -465,6 +466,58 @@ void main() { }); }); + group('SimpleSealedBase', () { + void roundTripSimpleSealed(SimpleSealedBase p) { + roundTripObject(p, SimpleSealedBase.fromJson); + } + + test('SimpleSealedImplOne', () { + roundTripSimpleSealed( + SimpleSealedImplOne( + testFieldOne: 'test field one', + commonField: 'common field', + ), + ); + }); + + test('SimpleSealedImplTwo', () { + roundTripSimpleSealed( + SimpleSealedImplTwo( + testFieldTwo: 'test field two', + commonField: 'common field', + ), + ); + }); + }); + + group('SealedWithDiscriminatorAndRenameBase', () { + void roundTripSimpleSealedWithDiscriminatorAndRename( + SealedWithDiscriminatorAndRenameBase p) { + roundTripObject( + p, + SealedWithDiscriminatorAndRenameBase.fromJson, + ); + } + + test('SealedWithDiscriminatorAndRenameImplOne', () { + roundTripSimpleSealedWithDiscriminatorAndRename( + SealedWithDiscriminatorAndRenameImplOne( + 42, + 42, + ), + ); + }); + + test('SealedWithDiscriminatorAndRenameImplTwo', () { + roundTripSimpleSealedWithDiscriminatorAndRename( + SealedWithDiscriminatorAndRenameImplTwo( + 42, + 42, + ), + ); + }); + }); + test('Issue1226Regression', () { final instance = Issue1226Regression(durationType: null); expect(instance.toJson(), isEmpty); diff --git a/json_serializable/test/integration/sealed_class_examples.dart b/json_serializable/test/integration/sealed_class_examples.dart new file mode 100644 index 00000000..4df43f6f --- /dev/null +++ b/json_serializable/test/integration/sealed_class_examples.dart @@ -0,0 +1,115 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'sealed_class_examples.g.dart'; + +@JsonSerializable() +sealed class SimpleSealedBase { + final String commonField; + + SimpleSealedBase({required this.commonField}); + + factory SimpleSealedBase.fromJson(Map json) => + _$SimpleSealedBaseFromJson(json); + + Map toJson() => _$SimpleSealedBaseToJson(this); +} + +@JsonSerializable() +class SimpleSealedImplOne extends SimpleSealedBase { + final String testFieldOne; + + SimpleSealedImplOne({ + required this.testFieldOne, + required super.commonField, + }); + + @override + bool operator ==(Object other) => + other is SimpleSealedImplOne && + testFieldOne == other.testFieldOne && + commonField == other.commonField; + + @override + int get hashCode => identityHashCode(this); +} + +@JsonSerializable() +class SimpleSealedImplTwo extends SimpleSealedBase { + final String testFieldTwo; + + SimpleSealedImplTwo({ + required this.testFieldTwo, + required super.commonField, + }); + + @override + bool operator ==(Object other) => + other is SimpleSealedImplTwo && + testFieldTwo == other.testFieldTwo && + commonField == other.commonField; + + @override + int get hashCode => identityHashCode(this); +} + +@JsonSerializable( + unionDiscriminator: 'custom_discriminator', + unionRename: UnionRename.snake, +) +sealed class SealedWithDiscriminatorAndRenameBase { + final int common; + + SealedWithDiscriminatorAndRenameBase(this.common); + + factory SealedWithDiscriminatorAndRenameBase.fromJson( + Map json) => + _$SealedWithDiscriminatorAndRenameBaseFromJson(json); + + Map toJson() => + _$SealedWithDiscriminatorAndRenameBaseToJson(this); +} + +@JsonSerializable() +class SealedWithDiscriminatorAndRenameImplOne + extends SealedWithDiscriminatorAndRenameBase { + final int testOne; + + SealedWithDiscriminatorAndRenameImplOne( + this.testOne, + super.common, + ); + + @override + bool operator ==(Object other) => + other is SealedWithDiscriminatorAndRenameImplOne && + testOne == other.testOne && + common == other.common; + + @override + int get hashCode => identityHashCode(this); +} + +@JsonSerializable() +class SealedWithDiscriminatorAndRenameImplTwo + extends SealedWithDiscriminatorAndRenameBase { + final int testTwo; + + SealedWithDiscriminatorAndRenameImplTwo( + this.testTwo, + super.common, + ); + + @override + bool operator ==(Object other) => + other is SealedWithDiscriminatorAndRenameImplTwo && + testTwo == other.testTwo && + common == other.common; + + @override + int get hashCode => identityHashCode(this); +} + +// TODO(@O-Hannonen): Add more examples for round trip cases, add at least: +// - Nested sealed classes +// - Sealed classes with itself as instance field (nullable?) +// - Subclasses that implement multiple sealed classes diff --git a/json_serializable/test/integration/sealed_class_examples.g.dart b/json_serializable/test/integration/sealed_class_examples.g.dart new file mode 100644 index 00000000..96b70d2d --- /dev/null +++ b/json_serializable/test/integration/sealed_class_examples.g.dart @@ -0,0 +1,117 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: lines_longer_than_80_chars, text_direction_code_point_in_literal, inference_failure_on_function_invocation, inference_failure_on_collection_literal + +part of 'sealed_class_examples.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SimpleSealedBase _$SimpleSealedBaseFromJson(Map json) => + switch (json['type']) { + 'SimpleSealedImplOne' => _$SimpleSealedImplOneFromJson(json), + 'SimpleSealedImplTwo' => _$SimpleSealedImplTwoFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SimpleSealedBase, + json, + ), + }; + +Map _$SimpleSealedBaseToJson(SimpleSealedBase instance) => + switch (instance) { + final SimpleSealedImplOne instance => { + 'type': 'SimpleSealedImplOne', + ..._$SimpleSealedImplOneToJson(instance), + }, + final SimpleSealedImplTwo instance => { + 'type': 'SimpleSealedImplTwo', + ..._$SimpleSealedImplTwoToJson(instance), + }, + }; + +SimpleSealedImplOne _$SimpleSealedImplOneFromJson(Map json) => + SimpleSealedImplOne( + testFieldOne: json['testFieldOne'] as String, + commonField: json['commonField'] as String, + ); + +Map _$SimpleSealedImplOneToJson( + SimpleSealedImplOne instance) => + { + 'commonField': instance.commonField, + 'testFieldOne': instance.testFieldOne, + }; + +SimpleSealedImplTwo _$SimpleSealedImplTwoFromJson(Map json) => + SimpleSealedImplTwo( + testFieldTwo: json['testFieldTwo'] as String, + commonField: json['commonField'] as String, + ); + +Map _$SimpleSealedImplTwoToJson( + SimpleSealedImplTwo instance) => + { + 'commonField': instance.commonField, + 'testFieldTwo': instance.testFieldTwo, + }; + +SealedWithDiscriminatorAndRenameBase + _$SealedWithDiscriminatorAndRenameBaseFromJson(Map json) => + switch (json['custom_discriminator']) { + 'sealed_with_discriminator_and_rename_impl_one' => + _$SealedWithDiscriminatorAndRenameImplOneFromJson(json), + 'sealed_with_discriminator_and_rename_impl_two' => + _$SealedWithDiscriminatorAndRenameImplTwoFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['custom_discriminator']}', + SealedWithDiscriminatorAndRenameBase, + json, + ), + }; + +Map _$SealedWithDiscriminatorAndRenameBaseToJson( + SealedWithDiscriminatorAndRenameBase instance) => + switch (instance) { + final SealedWithDiscriminatorAndRenameImplOne instance => { + 'custom_discriminator': + 'sealed_with_discriminator_and_rename_impl_one', + ..._$SealedWithDiscriminatorAndRenameImplOneToJson(instance), + }, + final SealedWithDiscriminatorAndRenameImplTwo instance => { + 'custom_discriminator': + 'sealed_with_discriminator_and_rename_impl_two', + ..._$SealedWithDiscriminatorAndRenameImplTwoToJson(instance), + }, + }; + +SealedWithDiscriminatorAndRenameImplOne + _$SealedWithDiscriminatorAndRenameImplOneFromJson( + Map json) => + SealedWithDiscriminatorAndRenameImplOne( + (json['testOne'] as num).toInt(), + (json['common'] as num).toInt(), + ); + +Map _$SealedWithDiscriminatorAndRenameImplOneToJson( + SealedWithDiscriminatorAndRenameImplOne instance) => + { + 'common': instance.common, + 'testOne': instance.testOne, + }; + +SealedWithDiscriminatorAndRenameImplTwo + _$SealedWithDiscriminatorAndRenameImplTwoFromJson( + Map json) => + SealedWithDiscriminatorAndRenameImplTwo( + (json['testTwo'] as num).toInt(), + (json['common'] as num).toInt(), + ); + +Map _$SealedWithDiscriminatorAndRenameImplTwoToJson( + SealedWithDiscriminatorAndRenameImplTwo instance) => + { + 'common': instance.common, + 'testTwo': instance.testTwo, + }; diff --git a/json_serializable/test/src/sealed_test_input.dart b/json_serializable/test/src/sealed_test_input.dart index cc184c44..5afbca24 100644 --- a/json_serializable/test/src/sealed_test_input.dart +++ b/json_serializable/test/src/sealed_test_input.dart @@ -763,17 +763,3 @@ class SubFourMultipleImpl SubFourMultipleImpl(this.subFourField); } - - - - -/// -/// -/// Happy cases: -/// - Simple with two subclasses -/// - Simple with changed discriminator -/// - Simple with union rename -/// - Simple with changed discriminator and union rename -/// - Complex nested -/// - Complex many implementations -/// From 3b226729202e7a1beebb06abeb82453833103159 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Mon, 7 Apr 2025 16:35:47 +0300 Subject: [PATCH 10/18] test: add integration tests and refine examples --- .../lib/complex_sealed_class_examples.dart | 84 ++-- .../lib/complex_sealed_class_examples.g.dart | 120 ++---- example/lib/sealed_class_example.dart | 38 +- example/lib/sealed_class_example.g.dart | 54 ++- .../test/integration/integration_test.dart | 104 +++-- .../integration/sealed_class_examples.dart | 380 +++++++++++++++--- .../integration/sealed_class_examples.g.dart | 357 ++++++++++++---- 7 files changed, 761 insertions(+), 376 deletions(-) diff --git a/example/lib/complex_sealed_class_examples.dart b/example/lib/complex_sealed_class_examples.dart index f66a6d71..df8277a2 100644 --- a/example/lib/complex_sealed_class_examples.dart +++ b/example/lib/complex_sealed_class_examples.dart @@ -3,77 +3,41 @@ import 'package:json_annotation/json_annotation.dart'; part 'complex_sealed_class_examples.g.dart'; @JsonSerializable( - unionDiscriminator: 'base_base_base_type', + unionDiscriminator: 'organization', ) -sealed class BaseBaseBaseType { - BaseBaseBaseType(); +sealed class Organization { + final String name; - factory BaseBaseBaseType.fromJson(Map json) => - _$BaseBaseBaseTypeFromJson(json); + Organization({required this.name}); - Map toJson() => _$BaseBaseBaseTypeToJson(this); -} + factory Organization.fromJson(Map json) => + _$OrganizationFromJson(json); -@JsonSerializable( - unionDiscriminator: 'base_base_type', -) -sealed class BaseBase extends BaseBaseBaseType { - BaseBase(); + Map toJson() => _$OrganizationToJson(this); } @JsonSerializable( - unionDiscriminator: 'base_type', + unionDiscriminator: 'department', ) -sealed class Base extends BaseBase { - Base(); -} +sealed class Department extends Organization { + final String departmentHead; -@JsonSerializable() -class FirstBaseImpl extends Base { - FirstBaseImpl(this.value); + Department({ + required this.departmentHead, + required super.name, + }); - String value; + factory Department.fromJson(Map json) => + _$DepartmentFromJson(json); } @JsonSerializable() -class SecondBaseImpl extends Base { - SecondBaseImpl(this.value); - - String value; -} - -@JsonSerializable() -class BaseBaseImpl extends BaseBase { - BaseBaseImpl(this.value); - - String value; -} - -@JsonSerializable( - createToJson: false, -) -sealed class SecondBase { - SecondBase(); - - factory SecondBase.fromJson(Map json) => - _$SecondBaseFromJson(json); -} - -@JsonSerializable( - createToJson: false, -) -sealed class ThirdBase { - ThirdBase(); - - factory ThirdBase.fromJson(Map json) => - _$ThirdBaseFromJson(json); -} - -@JsonSerializable( - createToJson: false, -) -class ImplAll implements SecondBase, ThirdBase { - ImplAll(this.value); - - String value; +class Team extends Department { + final String teamLead; + + Team({ + required this.teamLead, + required super.departmentHead, + required super.name, + }); } diff --git a/example/lib/complex_sealed_class_examples.g.dart b/example/lib/complex_sealed_class_examples.g.dart index df33bbb1..b39e7b90 100644 --- a/example/lib/complex_sealed_class_examples.g.dart +++ b/example/lib/complex_sealed_class_examples.g.dart @@ -6,116 +6,50 @@ part of 'complex_sealed_class_examples.dart'; // JsonSerializableGenerator // ************************************************************************** -BaseBaseBaseType _$BaseBaseBaseTypeFromJson(Map json) => - switch (json['base_base_base_type']) { - 'BaseBase' => _$BaseBaseFromJson(json), +Organization _$OrganizationFromJson(Map json) => + switch (json['organization']) { + 'Department' => _$DepartmentFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['base_base_base_type']}', - BaseBaseBaseType, + '${json['organization']}', + Organization, json, ), }; -Map _$BaseBaseBaseTypeToJson(BaseBaseBaseType instance) => +Map _$OrganizationToJson(Organization instance) => switch (instance) { - final BaseBase instance => { - 'base_base_base_type': 'BaseBase', - ..._$BaseBaseToJson(instance), + final Department instance => { + 'organization': 'Department', + ..._$DepartmentToJson(instance), }, }; -BaseBase _$BaseBaseFromJson(Map json) => - switch (json['base_base_type']) { - 'Base' => _$BaseFromJson(json), - 'BaseBaseImpl' => _$BaseBaseImplFromJson(json), +Department _$DepartmentFromJson(Map json) => + switch (json['department']) { + 'Team' => _$TeamFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['base_base_type']}', - BaseBase, + '${json['department']}', + Department, json, ), }; -Map _$BaseBaseToJson(BaseBase instance) => switch (instance) { - final Base instance => { - 'base_base_type': 'Base', - ..._$BaseToJson(instance), - }, - final BaseBaseImpl instance => { - 'base_base_type': 'BaseBaseImpl', - ..._$BaseBaseImplToJson(instance), - }, - }; - -Base _$BaseFromJson(Map json) => switch (json['base_type']) { - 'FirstBaseImpl' => _$FirstBaseImplFromJson(json), - 'SecondBaseImpl' => _$SecondBaseImplFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['base_type']}', - Base, - json, - ), - }; - -Map _$BaseToJson(Base instance) => switch (instance) { - final FirstBaseImpl instance => { - 'base_type': 'FirstBaseImpl', - ..._$FirstBaseImplToJson(instance), - }, - final SecondBaseImpl instance => { - 'base_type': 'SecondBaseImpl', - ..._$SecondBaseImplToJson(instance), +Map _$DepartmentToJson(Department instance) => + switch (instance) { + final Team instance => { + 'department': 'Team', + ..._$TeamToJson(instance), }, }; -FirstBaseImpl _$FirstBaseImplFromJson(Map json) => - FirstBaseImpl( - json['value'] as String, - ); - -Map _$FirstBaseImplToJson(FirstBaseImpl instance) => - { - 'value': instance.value, - }; - -SecondBaseImpl _$SecondBaseImplFromJson(Map json) => - SecondBaseImpl( - json['value'] as String, +Team _$TeamFromJson(Map json) => Team( + teamLead: json['teamLead'] as String, + departmentHead: json['departmentHead'] as String, + name: json['name'] as String, ); -Map _$SecondBaseImplToJson(SecondBaseImpl instance) => - { - 'value': instance.value, +Map _$TeamToJson(Team instance) => { + 'name': instance.name, + 'departmentHead': instance.departmentHead, + 'teamLead': instance.teamLead, }; - -BaseBaseImpl _$BaseBaseImplFromJson(Map json) => BaseBaseImpl( - json['value'] as String, - ); - -Map _$BaseBaseImplToJson(BaseBaseImpl instance) => - { - 'value': instance.value, - }; - -SecondBase _$SecondBaseFromJson(Map json) => - switch (json['type']) { - 'ImplAll' => _$ImplAllFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SecondBase, - json, - ), - }; - -ThirdBase _$ThirdBaseFromJson(Map json) => - switch (json['type']) { - 'ImplAll' => _$ImplAllFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - ThirdBase, - json, - ), - }; - -ImplAll _$ImplAllFromJson(Map json) => ImplAll( - json['value'] as String, - ); diff --git a/example/lib/sealed_class_example.dart b/example/lib/sealed_class_example.dart index 6e56cda0..8265b511 100644 --- a/example/lib/sealed_class_example.dart +++ b/example/lib/sealed_class_example.dart @@ -3,36 +3,36 @@ import 'package:json_annotation/json_annotation.dart'; part 'sealed_class_example.g.dart'; @JsonSerializable( - unionDiscriminator: 'runtimeType', + unionDiscriminator: 'vehicle_type', unionRename: UnionRename.snake, ) -sealed class MySealedClass { - MySealedClass(this.value); +sealed class Vehicle { + final String vehicleID; - factory MySealedClass.fromJson(Map json) => - _$MySealedClassFromJson(json); + Vehicle({required this.vehicleID}); - String value; + factory Vehicle.fromJson(Map json) => + _$VehicleFromJson(json); - Map toJson() => _$MySealedClassToJson(this); + Map toJson() => _$VehicleToJson(this); } @JsonSerializable() -class FirstSubtype extends MySealedClass { - final String someAttribute; +class Car extends Vehicle { + final int numberOfDoors; - FirstSubtype( - this.someAttribute, - super.value, - ); + Car({ + required this.numberOfDoors, + required super.vehicleID, + }); } @JsonSerializable() -class SecondSubtype extends MySealedClass { - final String someOtherAttribute; +class Bicycle extends Vehicle { + final bool hasBell; - SecondSubtype( - this.someOtherAttribute, - super.value, - ); + Bicycle({ + required this.hasBell, + required super.vehicleID, + }); } diff --git a/example/lib/sealed_class_example.g.dart b/example/lib/sealed_class_example.g.dart index a50f0971..860699c8 100644 --- a/example/lib/sealed_class_example.g.dart +++ b/example/lib/sealed_class_example.g.dart @@ -6,48 +6,44 @@ part of 'sealed_class_example.dart'; // JsonSerializableGenerator // ************************************************************************** -MySealedClass _$MySealedClassFromJson(Map json) => - switch (json['runtimeType']) { - 'first_subtype' => _$FirstSubtypeFromJson(json), - 'second_subtype' => _$SecondSubtypeFromJson(json), +Vehicle _$VehicleFromJson(Map json) => + switch (json['vehicle_type']) { + 'car' => _$CarFromJson(json), + 'bicycle' => _$BicycleFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['runtimeType']}', - MySealedClass, + '${json['vehicle_type']}', + Vehicle, json, ), }; -Map _$MySealedClassToJson(MySealedClass instance) => - switch (instance) { - final FirstSubtype instance => { - 'runtimeType': 'first_subtype', - ..._$FirstSubtypeToJson(instance), +Map _$VehicleToJson(Vehicle instance) => switch (instance) { + final Car instance => { + 'vehicle_type': 'car', + ..._$CarToJson(instance), }, - final SecondSubtype instance => { - 'runtimeType': 'second_subtype', - ..._$SecondSubtypeToJson(instance), + final Bicycle instance => { + 'vehicle_type': 'bicycle', + ..._$BicycleToJson(instance), }, }; -FirstSubtype _$FirstSubtypeFromJson(Map json) => FirstSubtype( - json['someAttribute'] as String, - json['value'] as String, +Car _$CarFromJson(Map json) => Car( + numberOfDoors: (json['numberOfDoors'] as num).toInt(), + vehicleID: json['vehicleID'] as String, ); -Map _$FirstSubtypeToJson(FirstSubtype instance) => - { - 'value': instance.value, - 'someAttribute': instance.someAttribute, +Map _$CarToJson(Car instance) => { + 'vehicleID': instance.vehicleID, + 'numberOfDoors': instance.numberOfDoors, }; -SecondSubtype _$SecondSubtypeFromJson(Map json) => - SecondSubtype( - json['someOtherAttribute'] as String, - json['value'] as String, +Bicycle _$BicycleFromJson(Map json) => Bicycle( + hasBell: json['hasBell'] as bool, + vehicleID: json['vehicleID'] as String, ); -Map _$SecondSubtypeToJson(SecondSubtype instance) => - { - 'value': instance.value, - 'someOtherAttribute': instance.someOtherAttribute, +Map _$BicycleToJson(Bicycle instance) => { + 'vehicleID': instance.vehicleID, + 'hasBell': instance.hasBell, }; diff --git a/json_serializable/test/integration/integration_test.dart b/json_serializable/test/integration/integration_test.dart index d0ce8d41..fbe32cc3 100644 --- a/json_serializable/test/integration/integration_test.dart +++ b/json_serializable/test/integration/integration_test.dart @@ -87,7 +87,7 @@ void main() { ); }); - test('simple', () { + test('ssube', () { roundTripOrder( Order.custom(Category.top, [ Item(24) @@ -256,7 +256,7 @@ void main() { roundTripObject(p, Numbers.fromJson); } - test('simple', () { + test('ssube', () { roundTripNumber( Numbers() ..nums = [0, 0.0] @@ -466,56 +466,86 @@ void main() { }); }); - group('SimpleSealedBase', () { - void roundTripSimpleSealed(SimpleSealedBase p) { - roundTripObject(p, SimpleSealedBase.fromJson); + group('Vehicle', () { + void roundTripVehicle(Vehicle v) { + roundTripObject(v, Vehicle.fromJson); } - test('SimpleSealedImplOne', () { - roundTripSimpleSealed( - SimpleSealedImplOne( - testFieldOne: 'test field one', - commonField: 'common field', - ), - ); + test('Car', () { + roundTripVehicle(Car(numberOfDoors: 4, vehicleID: 'vehicle-123')); }); - test('SimpleSealedImplTwo', () { - roundTripSimpleSealed( - SimpleSealedImplTwo( - testFieldTwo: 'test field two', - commonField: 'common field', - ), - ); + test('Bicycle', () { + roundTripVehicle(Bicycle(hasBell: true, vehicleID: 'vehicle-456')); }); }); - group('SealedWithDiscriminatorAndRenameBase', () { - void roundTripSimpleSealedWithDiscriminatorAndRename( - SealedWithDiscriminatorAndRenameBase p) { - roundTripObject( - p, - SealedWithDiscriminatorAndRenameBase.fromJson, - ); + group('Delivery', () { + void roundTripDelivery(Delivery d) { + roundTripObject(d, Delivery.fromJson); } - test('SealedWithDiscriminatorAndRenameImplOne', () { - roundTripSimpleSealedWithDiscriminatorAndRename( - SealedWithDiscriminatorAndRenameImplOne( - 42, - 42, - ), + test('DroneDelivery', () { + roundTripDelivery(DroneDelivery(droneModel: 1, deliveryID: 789)); + }); + + test('TruckDelivery', () { + roundTripDelivery( + TruckDelivery(weightCapacity: 3000.0, deliveryID: 1011), ); }); + }); - test('SealedWithDiscriminatorAndRenameImplTwo', () { - roundTripSimpleSealedWithDiscriminatorAndRename( - SealedWithDiscriminatorAndRenameImplTwo( - 42, - 42, + group('Container', () { + void roundTripContainer(Container c) { + roundTripObject(c, Container.fromJson); + } + + test('Box with nested items', () { + roundTripContainer( + Box( + containerID: 'box-321', + items: [ + Product(name: 'Product-X', itemID: 'item-654'), + Equipment( + label: 'Type-Y', + itemID: 'item-987', + nestedContainer: Parcel( + containerID: 'nested-parcel-222', + items: [Product(name: 'Product-Y', itemID: 'item-876')], + ), + ), + ], ), ); }); + + test('Parcel without items', () { + roundTripContainer(Parcel(containerID: 'parcel-111')); + }); + }); + + group('Transportable and Trackable', () { + void roundTripTransportable(Transportable t) { + roundTripObject(t, Transportable.fromJson); + } + + void roundTripTrackable(Trackable t) { + roundTripObject(t, Trackable.fromJson); + } + + test('Ship', () { + roundTripTransportable(Ship(cargoCapacity: 5000.0)); + }); + + test('GPSDevice', () { + roundTripTrackable(GPSDevice(serialNumber: 'gps-12345')); + }); + + test('Package', () { + roundTripTransportable(Package(label: 'Package-123', weight: 2.5)); + roundTripTrackable(Package(label: 'Package-123', weight: 2.5)); + }); }); test('Issue1226Regression', () { diff --git a/json_serializable/test/integration/sealed_class_examples.dart b/json_serializable/test/integration/sealed_class_examples.dart index 4df43f6f..23a9fb35 100644 --- a/json_serializable/test/integration/sealed_class_examples.dart +++ b/json_serializable/test/integration/sealed_class_examples.dart @@ -1,115 +1,377 @@ +import 'dart:convert'; + import 'package:json_annotation/json_annotation.dart'; +import '../test_utils.dart'; + part 'sealed_class_examples.g.dart'; @JsonSerializable() -sealed class SimpleSealedBase { - final String commonField; +sealed class Vehicle { + final String vehicleID; - SimpleSealedBase({required this.commonField}); + Vehicle({required this.vehicleID}); - factory SimpleSealedBase.fromJson(Map json) => - _$SimpleSealedBaseFromJson(json); + factory Vehicle.fromJson(Map json) => + _$VehicleFromJson(json); - Map toJson() => _$SimpleSealedBaseToJson(this); + Map toJson() => _$VehicleToJson(this); } @JsonSerializable() -class SimpleSealedImplOne extends SimpleSealedBase { - final String testFieldOne; +class Car extends Vehicle { + final int numberOfDoors; - SimpleSealedImplOne({ - required this.testFieldOne, - required super.commonField, + Car({ + required this.numberOfDoors, + required super.vehicleID, }); @override bool operator ==(Object other) => - other is SimpleSealedImplOne && - testFieldOne == other.testFieldOne && - commonField == other.commonField; + identical(this, other) || + other is Car && + runtimeType == other.runtimeType && + numberOfDoors == other.numberOfDoors && + vehicleID == other.vehicleID; @override - int get hashCode => identityHashCode(this); + int get hashCode => jsonEncode(this).hashCode; } @JsonSerializable() -class SimpleSealedImplTwo extends SimpleSealedBase { - final String testFieldTwo; +class Bicycle extends Vehicle { + final bool hasBell; - SimpleSealedImplTwo({ - required this.testFieldTwo, - required super.commonField, + Bicycle({ + required this.hasBell, + required super.vehicleID, }); @override bool operator ==(Object other) => - other is SimpleSealedImplTwo && - testFieldTwo == other.testFieldTwo && - commonField == other.commonField; + identical(this, other) || + other is Bicycle && + runtimeType == other.runtimeType && + hasBell == other.hasBell && + vehicleID == other.vehicleID; @override - int get hashCode => identityHashCode(this); + int get hashCode => jsonEncode(this).hashCode; } @JsonSerializable( - unionDiscriminator: 'custom_discriminator', + unionDiscriminator: 'delivery_type', unionRename: UnionRename.snake, ) -sealed class SealedWithDiscriminatorAndRenameBase { - final int common; +sealed class Delivery { + final int deliveryID; + + Delivery({ + required this.deliveryID, + }); + + factory Delivery.fromJson(Map json) => + _$DeliveryFromJson(json); + + Map toJson() => _$DeliveryToJson(this); +} + +@JsonSerializable() +class DroneDelivery extends Delivery { + final int droneModel; + + DroneDelivery({ + required this.droneModel, + required super.deliveryID, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is DroneDelivery && + runtimeType == other.runtimeType && + droneModel == other.droneModel && + deliveryID == other.deliveryID; + + @override + int get hashCode => jsonEncode(this).hashCode; +} + +@JsonSerializable() +class TruckDelivery extends Delivery { + final double weightCapacity; + + TruckDelivery({ + required this.weightCapacity, + required super.deliveryID, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is TruckDelivery && + runtimeType == other.runtimeType && + weightCapacity == other.weightCapacity && + deliveryID == other.deliveryID; + + @override + int get hashCode => jsonEncode(this).hashCode; +} + +@JsonSerializable( + unionDiscriminator: 'organization', +) +sealed class Organization { + final String name; + + Organization({required this.name}); + + factory Organization.fromJson(Map json) => + _$OrganizationFromJson(json); + + Map toJson() => _$OrganizationToJson(this); +} + +@JsonSerializable( + unionDiscriminator: 'department', +) +sealed class Department extends Organization { + final String departmentHead; + + Department({ + required this.departmentHead, + required super.name, + }); + + factory Department.fromJson(Map json) => + _$DepartmentFromJson(json); +} + +@JsonSerializable() +class Team extends Department { + final String teamLead; + + Team({ + required this.teamLead, + required super.departmentHead, + required super.name, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Team && + runtimeType == other.runtimeType && + teamLead == other.teamLead && + departmentHead == other.departmentHead && + name == other.name; + + @override + int get hashCode => jsonEncode(this).hashCode; +} + +@JsonSerializable() +sealed class Transportable { + const Transportable(); + + factory Transportable.fromJson(Map json) => + _$TransportableFromJson(json); + + Map toJson(); +} + +@JsonSerializable() +sealed class Trackable { + const Trackable(); + + factory Trackable.fromJson(Map json) => + _$TrackableFromJson(json); + + Map toJson(); +} + +@JsonSerializable() +class Ship implements Transportable { + final double cargoCapacity; - SealedWithDiscriminatorAndRenameBase(this.common); + Ship({ + required this.cargoCapacity, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Ship && + runtimeType == other.runtimeType && + cargoCapacity == other.cargoCapacity; - factory SealedWithDiscriminatorAndRenameBase.fromJson( - Map json) => - _$SealedWithDiscriminatorAndRenameBaseFromJson(json); + @override + int get hashCode => jsonEncode(this).hashCode; - Map toJson() => - _$SealedWithDiscriminatorAndRenameBaseToJson(this); + @override + Map toJson() => _$TransportableToJson(this); } @JsonSerializable() -class SealedWithDiscriminatorAndRenameImplOne - extends SealedWithDiscriminatorAndRenameBase { - final int testOne; +class GPSDevice implements Trackable { + final String serialNumber; - SealedWithDiscriminatorAndRenameImplOne( - this.testOne, - super.common, - ); + GPSDevice({ + required this.serialNumber, + }); @override bool operator ==(Object other) => - other is SealedWithDiscriminatorAndRenameImplOne && - testOne == other.testOne && - common == other.common; + identical(this, other) || + other is GPSDevice && + runtimeType == other.runtimeType && + serialNumber == other.serialNumber; + + @override + int get hashCode => jsonEncode(this).hashCode; @override - int get hashCode => identityHashCode(this); + Map toJson() => _$TrackableToJson(this); } @JsonSerializable() -class SealedWithDiscriminatorAndRenameImplTwo - extends SealedWithDiscriminatorAndRenameBase { - final int testTwo; +class Package implements Transportable, Trackable { + final String label; + final double weight; - SealedWithDiscriminatorAndRenameImplTwo( - this.testTwo, - super.common, - ); + Package({ + required this.label, + required this.weight, + }); @override bool operator ==(Object other) => - other is SealedWithDiscriminatorAndRenameImplTwo && - testTwo == other.testTwo && - common == other.common; + identical(this, other) || + other is Package && + runtimeType == other.runtimeType && + label == other.label && + weight == other.weight; + + @override + int get hashCode => jsonEncode(this).hashCode; @override - int get hashCode => identityHashCode(this); + Map toJson() => _$TrackableToJson(this); +} + +@JsonSerializable() +sealed class Container { + final String containerID; + final List items; + + Container({ + required this.containerID, + this.items = const [], + }); + + factory Container.fromJson(Map json) => + _$ContainerFromJson(json); + + Map toJson() => _$ContainerToJson(this); +} + +@JsonSerializable() +sealed class StorageItem { + final String itemID; + final Container? nestedContainer; + + StorageItem({ + required this.itemID, + this.nestedContainer, + }); + + factory StorageItem.fromJson(Map json) => + _$StorageItemFromJson(json); + + Map toJson() => _$StorageItemToJson(this); } -// TODO(@O-Hannonen): Add more examples for round trip cases, add at least: -// - Nested sealed classes -// - Sealed classes with itself as instance field (nullable?) -// - Subclasses that implement multiple sealed classes +@JsonSerializable() +class Box extends Container { + Box({ + required super.containerID, + super.items, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Box && + runtimeType == other.runtimeType && + containerID == other.containerID && + deepEquals(items, other.items); + + @override + int get hashCode => jsonEncode(this).hashCode; +} + +@JsonSerializable() +class Parcel extends Container { + Parcel({ + required super.containerID, + super.items, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Parcel && + runtimeType == other.runtimeType && + containerID == other.containerID && + deepEquals(items, items); + + @override + int get hashCode => jsonEncode(this).hashCode; +} + +@JsonSerializable() +class Product extends StorageItem { + final String name; + + Product({ + required this.name, + required super.itemID, + super.nestedContainer, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Product && + runtimeType == other.runtimeType && + name == other.name && + itemID == other.itemID && + nestedContainer == other.nestedContainer; + + @override + int get hashCode => jsonEncode(this).hashCode; +} + +@JsonSerializable() +class Equipment extends StorageItem { + final String label; + + Equipment({ + required this.label, + required super.itemID, + super.nestedContainer, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Equipment && + runtimeType == other.runtimeType && + label == other.label && + itemID == other.itemID && + nestedContainer == other.nestedContainer; + + @override + int get hashCode => jsonEncode(this).hashCode; +} diff --git a/json_serializable/test/integration/sealed_class_examples.g.dart b/json_serializable/test/integration/sealed_class_examples.g.dart index 96b70d2d..cbc73561 100644 --- a/json_serializable/test/integration/sealed_class_examples.g.dart +++ b/json_serializable/test/integration/sealed_class_examples.g.dart @@ -8,110 +8,309 @@ part of 'sealed_class_examples.dart'; // JsonSerializableGenerator // ************************************************************************** -SimpleSealedBase _$SimpleSealedBaseFromJson(Map json) => - switch (json['type']) { - 'SimpleSealedImplOne' => _$SimpleSealedImplOneFromJson(json), - 'SimpleSealedImplTwo' => _$SimpleSealedImplTwoFromJson(json), +Vehicle _$VehicleFromJson(Map json) => switch (json['type']) { + 'Car' => _$CarFromJson(json), + 'Bicycle' => _$BicycleFromJson(json), _ => throw UnrecognizedUnionTypeException( '${json['type']}', - SimpleSealedBase, + Vehicle, json, ), }; -Map _$SimpleSealedBaseToJson(SimpleSealedBase instance) => - switch (instance) { - final SimpleSealedImplOne instance => { - 'type': 'SimpleSealedImplOne', - ..._$SimpleSealedImplOneToJson(instance), +Map _$VehicleToJson(Vehicle instance) => switch (instance) { + final Car instance => { + 'type': 'Car', + ..._$CarToJson(instance), + }, + final Bicycle instance => { + 'type': 'Bicycle', + ..._$BicycleToJson(instance), + }, + }; + +Car _$CarFromJson(Map json) => Car( + numberOfDoors: (json['numberOfDoors'] as num).toInt(), + vehicleID: json['vehicleID'] as String, + ); + +Map _$CarToJson(Car instance) => { + 'vehicleID': instance.vehicleID, + 'numberOfDoors': instance.numberOfDoors, + }; + +Bicycle _$BicycleFromJson(Map json) => Bicycle( + hasBell: json['hasBell'] as bool, + vehicleID: json['vehicleID'] as String, + ); + +Map _$BicycleToJson(Bicycle instance) => { + 'vehicleID': instance.vehicleID, + 'hasBell': instance.hasBell, + }; + +Delivery _$DeliveryFromJson(Map json) => + switch (json['delivery_type']) { + 'drone_delivery' => _$DroneDeliveryFromJson(json), + 'truck_delivery' => _$TruckDeliveryFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['delivery_type']}', + Delivery, + json, + ), + }; + +Map _$DeliveryToJson(Delivery instance) => switch (instance) { + final DroneDelivery instance => { + 'delivery_type': 'drone_delivery', + ..._$DroneDeliveryToJson(instance), }, - final SimpleSealedImplTwo instance => { - 'type': 'SimpleSealedImplTwo', - ..._$SimpleSealedImplTwoToJson(instance), + final TruckDelivery instance => { + 'delivery_type': 'truck_delivery', + ..._$TruckDeliveryToJson(instance), }, }; -SimpleSealedImplOne _$SimpleSealedImplOneFromJson(Map json) => - SimpleSealedImplOne( - testFieldOne: json['testFieldOne'] as String, - commonField: json['commonField'] as String, +DroneDelivery _$DroneDeliveryFromJson(Map json) => + DroneDelivery( + droneModel: (json['droneModel'] as num).toInt(), + deliveryID: (json['deliveryID'] as num).toInt(), ); -Map _$SimpleSealedImplOneToJson( - SimpleSealedImplOne instance) => +Map _$DroneDeliveryToJson(DroneDelivery instance) => { - 'commonField': instance.commonField, - 'testFieldOne': instance.testFieldOne, + 'deliveryID': instance.deliveryID, + 'droneModel': instance.droneModel, }; -SimpleSealedImplTwo _$SimpleSealedImplTwoFromJson(Map json) => - SimpleSealedImplTwo( - testFieldTwo: json['testFieldTwo'] as String, - commonField: json['commonField'] as String, +TruckDelivery _$TruckDeliveryFromJson(Map json) => + TruckDelivery( + weightCapacity: (json['weightCapacity'] as num).toDouble(), + deliveryID: (json['deliveryID'] as num).toInt(), ); -Map _$SimpleSealedImplTwoToJson( - SimpleSealedImplTwo instance) => +Map _$TruckDeliveryToJson(TruckDelivery instance) => { - 'commonField': instance.commonField, - 'testFieldTwo': instance.testFieldTwo, - }; - -SealedWithDiscriminatorAndRenameBase - _$SealedWithDiscriminatorAndRenameBaseFromJson(Map json) => - switch (json['custom_discriminator']) { - 'sealed_with_discriminator_and_rename_impl_one' => - _$SealedWithDiscriminatorAndRenameImplOneFromJson(json), - 'sealed_with_discriminator_and_rename_impl_two' => - _$SealedWithDiscriminatorAndRenameImplTwoFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['custom_discriminator']}', - SealedWithDiscriminatorAndRenameBase, - json, - ), - }; - -Map _$SealedWithDiscriminatorAndRenameBaseToJson( - SealedWithDiscriminatorAndRenameBase instance) => + 'deliveryID': instance.deliveryID, + 'weightCapacity': instance.weightCapacity, + }; + +Organization _$OrganizationFromJson(Map json) => + switch (json['organization']) { + 'Department' => _$DepartmentFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['organization']}', + Organization, + json, + ), + }; + +Map _$OrganizationToJson(Organization instance) => switch (instance) { - final SealedWithDiscriminatorAndRenameImplOne instance => { - 'custom_discriminator': - 'sealed_with_discriminator_and_rename_impl_one', - ..._$SealedWithDiscriminatorAndRenameImplOneToJson(instance), + final Department instance => { + 'organization': 'Department', + ..._$DepartmentToJson(instance), }, - final SealedWithDiscriminatorAndRenameImplTwo instance => { - 'custom_discriminator': - 'sealed_with_discriminator_and_rename_impl_two', - ..._$SealedWithDiscriminatorAndRenameImplTwoToJson(instance), + }; + +Department _$DepartmentFromJson(Map json) => + switch (json['department']) { + 'Team' => _$TeamFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['department']}', + Department, + json, + ), + }; + +Map _$DepartmentToJson(Department instance) => + switch (instance) { + final Team instance => { + 'department': 'Team', + ..._$TeamToJson(instance), }, }; -SealedWithDiscriminatorAndRenameImplOne - _$SealedWithDiscriminatorAndRenameImplOneFromJson( - Map json) => - SealedWithDiscriminatorAndRenameImplOne( - (json['testOne'] as num).toInt(), - (json['common'] as num).toInt(), - ); +Team _$TeamFromJson(Map json) => Team( + teamLead: json['teamLead'] as String, + departmentHead: json['departmentHead'] as String, + name: json['name'] as String, + ); -Map _$SealedWithDiscriminatorAndRenameImplOneToJson( - SealedWithDiscriminatorAndRenameImplOne instance) => - { - 'common': instance.common, - 'testOne': instance.testOne, +Map _$TeamToJson(Team instance) => { + 'name': instance.name, + 'departmentHead': instance.departmentHead, + 'teamLead': instance.teamLead, }; -SealedWithDiscriminatorAndRenameImplTwo - _$SealedWithDiscriminatorAndRenameImplTwoFromJson( - Map json) => - SealedWithDiscriminatorAndRenameImplTwo( - (json['testTwo'] as num).toInt(), - (json['common'] as num).toInt(), - ); +Transportable _$TransportableFromJson(Map json) => + switch (json['type']) { + 'Ship' => _$ShipFromJson(json), + 'Package' => _$PackageFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + Transportable, + json, + ), + }; -Map _$SealedWithDiscriminatorAndRenameImplTwoToJson( - SealedWithDiscriminatorAndRenameImplTwo instance) => - { - 'common': instance.common, - 'testTwo': instance.testTwo, +Map _$TransportableToJson(Transportable instance) => + switch (instance) { + final Ship instance => { + 'type': 'Ship', + ..._$ShipToJson(instance), + }, + final Package instance => { + 'type': 'Package', + ..._$PackageToJson(instance), + }, + }; + +Trackable _$TrackableFromJson(Map json) => + switch (json['type']) { + 'GPSDevice' => _$GPSDeviceFromJson(json), + 'Package' => _$PackageFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + Trackable, + json, + ), + }; + +Map _$TrackableToJson(Trackable instance) => + switch (instance) { + final GPSDevice instance => { + 'type': 'GPSDevice', + ..._$GPSDeviceToJson(instance), + }, + final Package instance => { + 'type': 'Package', + ..._$PackageToJson(instance), + }, + }; + +Ship _$ShipFromJson(Map json) => Ship( + cargoCapacity: (json['cargoCapacity'] as num).toDouble(), + ); + +Map _$ShipToJson(Ship instance) => { + 'cargoCapacity': instance.cargoCapacity, + }; + +GPSDevice _$GPSDeviceFromJson(Map json) => GPSDevice( + serialNumber: json['serialNumber'] as String, + ); + +Map _$GPSDeviceToJson(GPSDevice instance) => { + 'serialNumber': instance.serialNumber, + }; + +Package _$PackageFromJson(Map json) => Package( + label: json['label'] as String, + weight: (json['weight'] as num).toDouble(), + ); + +Map _$PackageToJson(Package instance) => { + 'label': instance.label, + 'weight': instance.weight, + }; + +Container _$ContainerFromJson(Map json) => + switch (json['type']) { + 'Box' => _$BoxFromJson(json), + 'Parcel' => _$ParcelFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + Container, + json, + ), + }; + +Map _$ContainerToJson(Container instance) => + switch (instance) { + final Box instance => { + 'type': 'Box', + ..._$BoxToJson(instance), + }, + final Parcel instance => { + 'type': 'Parcel', + ..._$ParcelToJson(instance), + }, + }; + +StorageItem _$StorageItemFromJson(Map json) => + switch (json['type']) { + 'Product' => _$ProductFromJson(json), + 'Equipment' => _$EquipmentFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + StorageItem, + json, + ), + }; + +Map _$StorageItemToJson(StorageItem instance) => + switch (instance) { + final Product instance => { + 'type': 'Product', + ..._$ProductToJson(instance), + }, + final Equipment instance => { + 'type': 'Equipment', + ..._$EquipmentToJson(instance), + }, + }; + +Box _$BoxFromJson(Map json) => Box( + containerID: json['containerID'] as String, + items: (json['items'] as List?) + ?.map((e) => StorageItem.fromJson(e as Map)) + .toList() ?? + const [], + ); + +Map _$BoxToJson(Box instance) => { + 'containerID': instance.containerID, + 'items': instance.items, + }; + +Parcel _$ParcelFromJson(Map json) => Parcel( + containerID: json['containerID'] as String, + items: (json['items'] as List?) + ?.map((e) => StorageItem.fromJson(e as Map)) + .toList() ?? + const [], + ); + +Map _$ParcelToJson(Parcel instance) => { + 'containerID': instance.containerID, + 'items': instance.items, + }; + +Product _$ProductFromJson(Map json) => Product( + name: json['name'] as String, + itemID: json['itemID'] as String, + nestedContainer: json['nestedContainer'] == null + ? null + : Container.fromJson(json['nestedContainer'] as Map), + ); + +Map _$ProductToJson(Product instance) => { + 'itemID': instance.itemID, + 'nestedContainer': instance.nestedContainer, + 'name': instance.name, + }; + +Equipment _$EquipmentFromJson(Map json) => Equipment( + label: json['label'] as String, + itemID: json['itemID'] as String, + nestedContainer: json['nestedContainer'] == null + ? null + : Container.fromJson(json['nestedContainer'] as Map), + ); + +Map _$EquipmentToJson(Equipment instance) => { + 'itemID': instance.itemID, + 'nestedContainer': instance.nestedContainer, + 'label': instance.label, }; From 30847d5b54440fbd6eebddfc4c2debfd9c853189 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Mon, 7 Apr 2025 21:22:35 +0300 Subject: [PATCH 11/18] docs: bump version number and update documentation --- example/pubspec.yaml | 4 +- json_annotation/CHANGELOG.md | 5 ++ json_annotation/pubspec.yaml | 2 +- json_serializable/CHANGELOG.md | 5 ++ json_serializable/README.md | 67 ++++++++++++++++--- json_serializable/example/sealed_example.dart | 31 +++++++++ .../example/sealed_example.g.dart | 50 ++++++++++++++ json_serializable/pubspec.yaml | 4 +- .../tool/readme/readme_template.md | 15 +++++ json_serializable/tool/readme_builder.dart | 1 + 10 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 json_serializable/example/sealed_example.dart create mode 100644 json_serializable/example/sealed_example.g.dart diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 392a1cd2..dc4791ea 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -7,7 +7,7 @@ environment: resolution: workspace dependencies: - json_annotation: ^4.9.0 + json_annotation: ^4.10.0 dev_dependencies: # Used by tests. Not required to use `json_serializable`. @@ -21,7 +21,7 @@ dev_dependencies: build_verify: ^3.0.0 # REQUIRED! - json_serializable: ^6.8.0 + json_serializable: ^6.10.0 # Not required to use `json_serializable`. path: ^1.8.0 diff --git a/json_annotation/CHANGELOG.md b/json_annotation/CHANGELOG.md index ae0b4d43..f3c59b34 100644 --- a/json_annotation/CHANGELOG.md +++ b/json_annotation/CHANGELOG.md @@ -1,3 +1,8 @@ +## 4.10.0 + +- Add `JsonSerializable.unionRename` +- Add `JsonSerializable.unionDiscriminator` + ## 4.9.1-wip - Require Dart 3.8 diff --git a/json_annotation/pubspec.yaml b/json_annotation/pubspec.yaml index f1cd2c79..5a53ff2d 100644 --- a/json_annotation/pubspec.yaml +++ b/json_annotation/pubspec.yaml @@ -1,5 +1,5 @@ name: json_annotation -version: 4.9.1-wip +version: 4.10.0 description: >- Classes and helper functions that support JSON code generation via the `json_serializable` package. diff --git a/json_serializable/CHANGELOG.md b/json_serializable/CHANGELOG.md index 43ab453d..7876c273 100644 --- a/json_serializable/CHANGELOG.md +++ b/json_serializable/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.10.0 + +- Add support for deserializing union json to sealed class +- Add support for serializing sealed class to union json + ## 6.9.6-wip - Move `package:collection` to a dev dependency. diff --git a/json_serializable/README.md b/json_serializable/README.md index e31c6dc1..a739d2f7 100644 --- a/json_serializable/README.md +++ b/json_serializable/README.md @@ -253,6 +253,53 @@ customize the encoding/decoding of any type, you have a few options. } ``` +# Sealed classes + +As of [`json_serializable`] version 6.10.0 and [`json_annotation`] +version 4.10.0, sealed classes can be serialized to json unions and json unions +can be deserialized to sealed classes. + +To achieve this, both the sealed class and its subclasses should be annotated +with `JsonSerializable`. Only the sealed class should have `fromJson` factory +and `toJson` functions. To customize the sealed class behavior, use the fields +`unionRename` and `unionDiscriminator` in `JsonSerializable` or adjust the +default behavior by changing the corresponding fields in `build.yaml`. For +more complex examples, please see [example]: + +```dart +import 'package:json_annotation/json_annotation.dart'; + +part 'sealed_example.g.dart'; + +@JsonSerializable() +sealed class SealedBase { + const SealedBase(); + + factory SealedBase.fromJson(Map json) => + _$SealedBaseFromJson(json); + + Map toJson() => _$SealedBaseToJson(this); +} + +@JsonSerializable() +class SealedSub1 extends SealedBase { + final String exampleField1; + + SealedSub1({ + required this.exampleField1, + }); +} + +@JsonSerializable() +class SealedSub2 extends SealedBase { + final String exampleField2; + + SealedSub2({ + required this.exampleField2, + }); +} +``` + # Build configuration Aside from setting arguments on the associated annotation classes, you can also @@ -309,15 +356,17 @@ targets: [`Enum`]: https://api.dart.dev/dart-core/Enum-class.html [`int`]: https://api.dart.dev/dart-core/int-class.html [`Iterable`]: https://api.dart.dev/dart-core/Iterable-class.html -[`JsonConverter`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html -[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonEnum/valueField.html -[`JsonEnum`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonEnum-class.html -[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html -[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html -[`JsonKey`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey-class.html -[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonLiteral-class.html -[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable-class.html -[`JsonValue`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonValue-class.html +[`json_annotation`]: https://unknown.com/package/json_annotation +[`json_serializable`]: https://unknown.com/package/json_serializable +[`JsonConverter`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonConverter-class.html +[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonEnum/valueField.html +[`JsonEnum`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonEnum-class.html +[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonKey/fromJson.html +[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonKey/toJson.html +[`JsonKey`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonKey-class.html +[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonLiteral-class.html +[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonSerializable-class.html +[`JsonValue`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonValue-class.html [`List`]: https://api.dart.dev/dart-core/List-class.html [`Map`]: https://api.dart.dev/dart-core/Map-class.html [`num`]: https://api.dart.dev/dart-core/num-class.html diff --git a/json_serializable/example/sealed_example.dart b/json_serializable/example/sealed_example.dart new file mode 100644 index 00000000..17290620 --- /dev/null +++ b/json_serializable/example/sealed_example.dart @@ -0,0 +1,31 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'sealed_example.g.dart'; + +@JsonSerializable() +sealed class SealedBase { + const SealedBase(); + + factory SealedBase.fromJson(Map json) => + _$SealedBaseFromJson(json); + + Map toJson() => _$SealedBaseToJson(this); +} + +@JsonSerializable() +class SealedSub1 extends SealedBase { + final String exampleField1; + + SealedSub1({ + required this.exampleField1, + }); +} + +@JsonSerializable() +class SealedSub2 extends SealedBase { + final String exampleField2; + + SealedSub2({ + required this.exampleField2, + }); +} diff --git a/json_serializable/example/sealed_example.g.dart b/json_serializable/example/sealed_example.g.dart new file mode 100644 index 00000000..e07877dd --- /dev/null +++ b/json_serializable/example/sealed_example.g.dart @@ -0,0 +1,50 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: lines_longer_than_80_chars, text_direction_code_point_in_literal, inference_failure_on_function_invocation, inference_failure_on_collection_literal + +part of 'sealed_example.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SealedBase _$SealedBaseFromJson(Map json) => + switch (json['type']) { + 'SealedSub1' => _$SealedSub1FromJson(json), + 'SealedSub2' => _$SealedSub2FromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SealedBase, + json, + ), + }; + +Map _$SealedBaseToJson(SealedBase instance) => + switch (instance) { + final SealedSub1 instance => { + 'type': 'SealedSub1', + ..._$SealedSub1ToJson(instance), + }, + final SealedSub2 instance => { + 'type': 'SealedSub2', + ..._$SealedSub2ToJson(instance), + }, + }; + +SealedSub1 _$SealedSub1FromJson(Map json) => SealedSub1( + exampleField1: json['exampleField1'] as String, + ); + +Map _$SealedSub1ToJson(SealedSub1 instance) => + { + 'exampleField1': instance.exampleField1, + }; + +SealedSub2 _$SealedSub2FromJson(Map json) => SealedSub2( + exampleField2: json['exampleField2'] as String, + ); + +Map _$SealedSub2ToJson(SealedSub2 instance) => + { + 'exampleField2': instance.exampleField2, + }; diff --git a/json_serializable/pubspec.yaml b/json_serializable/pubspec.yaml index ec6022f1..343e1cda 100644 --- a/json_serializable/pubspec.yaml +++ b/json_serializable/pubspec.yaml @@ -1,5 +1,5 @@ name: json_serializable -version: 6.9.6-wip +version: 6.10.0 description: >- Automatically generate code for converting to and from JSON by annotating Dart classes. @@ -23,7 +23,7 @@ dependencies: # Use a tight version constraint to ensure that a constraint on # `json_annotation` properly constrains all features it provides. - json_annotation: '>=4.9.0 <4.10.0' + json_annotation: '>=4.10.0 <4.11.0' meta: ^1.14.0 path: ^1.9.0 pub_semver: ^2.1.4 diff --git a/json_serializable/tool/readme/readme_template.md b/json_serializable/tool/readme/readme_template.md index 3dacbcca..400fb086 100644 --- a/json_serializable/tool/readme/readme_template.md +++ b/json_serializable/tool/readme/readme_template.md @@ -121,6 +121,21 @@ customize the encoding/decoding of any type, you have a few options. +# Sealed classes + +As of `package:json_serializable` version 6.10.0 and `package:json_annotation` +version 4.10.0, sealed classes can be serialized to json unions and json unions +can be deserialized to sealed classes. + +To achieve this, both the sealed class and its subclasses should be annotated +with `JsonSerializable`. Only the sealed class should have `fromJson` factory +or `toJson` function. To customize the sealed class behavior, use the fields +`unionRename` and `unionDiscriminator` in `JsonSerializable` or adjust the +default behavior by changing the corresponding fields in `build.yaml`. For +more complex examples, please see [example]: + + + # Build configuration Aside from setting arguments on the associated annotation classes, you can also diff --git a/json_serializable/tool/readme_builder.dart b/json_serializable/tool/readme_builder.dart index 3709ee01..4807d625 100644 --- a/json_serializable/tool/readme_builder.dart +++ b/json_serializable/tool/readme_builder.dart @@ -26,6 +26,7 @@ class _ReadmeBuilder extends Builder { ...await buildStep.getExampleContent('example/example.dart'), ...await buildStep.getExampleContent('example/example.g.dart'), ...await buildStep.getExampleContent('tool/readme/readme_examples.dart'), + ...await buildStep.getExampleContent('example/sealed_example.dart'), 'supported_types': _classCleanAndSort(supportedTypes()), 'collection_types': _classCleanAndSort(collectionTypes()), 'map_key_types': _classCleanAndSort(mapKeyTypes), From 2c7354dafeae7df657ec9e1cc779cb2cde86d160 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Mon, 7 Apr 2025 21:40:12 +0300 Subject: [PATCH 12/18] fix: own review fixes --- json_serializable/README.md | 10 ++++------ json_serializable/lib/src/decode_helper.dart | 1 - .../test/integration/integration_test.dart | 4 ++-- json_serializable/tool/readme/readme_template.md | 6 +++--- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/json_serializable/README.md b/json_serializable/README.md index a739d2f7..bf832ce9 100644 --- a/json_serializable/README.md +++ b/json_serializable/README.md @@ -255,14 +255,14 @@ customize the encoding/decoding of any type, you have a few options. # Sealed classes -As of [`json_serializable`] version 6.10.0 and [`json_annotation`] +As of `json_serializable` version 6.10.0 and `json_annotation` version 4.10.0, sealed classes can be serialized to json unions and json unions can be deserialized to sealed classes. To achieve this, both the sealed class and its subclasses should be annotated -with `JsonSerializable`. Only the sealed class should have `fromJson` factory -and `toJson` functions. To customize the sealed class behavior, use the fields -`unionRename` and `unionDiscriminator` in `JsonSerializable` or adjust the +with [`JsonSerializable`]. Only the sealed class should have `fromJson` factory +or `toJson` function. To customize the sealed class behavior, use the fields +`unionRename` and `unionDiscriminator` in [`JsonSerializable`] or adjust the default behavior by changing the corresponding fields in `build.yaml`. For more complex examples, please see [example]: @@ -356,8 +356,6 @@ targets: [`Enum`]: https://api.dart.dev/dart-core/Enum-class.html [`int`]: https://api.dart.dev/dart-core/int-class.html [`Iterable`]: https://api.dart.dev/dart-core/Iterable-class.html -[`json_annotation`]: https://unknown.com/package/json_annotation -[`json_serializable`]: https://unknown.com/package/json_serializable [`JsonConverter`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonConverter-class.html [`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonEnum/valueField.html [`JsonEnum`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonEnum-class.html diff --git a/json_serializable/lib/src/decode_helper.dart b/json_serializable/lib/src/decode_helper.dart index 8de762db..688a09d8 100644 --- a/json_serializable/lib/src/decode_helper.dart +++ b/json_serializable/lib/src/decode_helper.dart @@ -278,7 +278,6 @@ _ => throw UnrecognizedUnionTypeException( /// ) /// ] /// ``` - /// List _createDefaultFunctionBody( _ConstructorData data, List checks, diff --git a/json_serializable/test/integration/integration_test.dart b/json_serializable/test/integration/integration_test.dart index fbe32cc3..6fc4e665 100644 --- a/json_serializable/test/integration/integration_test.dart +++ b/json_serializable/test/integration/integration_test.dart @@ -87,7 +87,7 @@ void main() { ); }); - test('ssube', () { + test('simple', () { roundTripOrder( Order.custom(Category.top, [ Item(24) @@ -256,7 +256,7 @@ void main() { roundTripObject(p, Numbers.fromJson); } - test('ssube', () { + test('simple', () { roundTripNumber( Numbers() ..nums = [0, 0.0] diff --git a/json_serializable/tool/readme/readme_template.md b/json_serializable/tool/readme/readme_template.md index 400fb086..19c4af8e 100644 --- a/json_serializable/tool/readme/readme_template.md +++ b/json_serializable/tool/readme/readme_template.md @@ -123,14 +123,14 @@ customize the encoding/decoding of any type, you have a few options. # Sealed classes -As of `package:json_serializable` version 6.10.0 and `package:json_annotation` +As of `json_serializable` version 6.10.0 and `json_annotation` version 4.10.0, sealed classes can be serialized to json unions and json unions can be deserialized to sealed classes. To achieve this, both the sealed class and its subclasses should be annotated -with `JsonSerializable`. Only the sealed class should have `fromJson` factory +with `ja:JsonSerializable`. Only the sealed class should have `fromJson` factory or `toJson` function. To customize the sealed class behavior, use the fields -`unionRename` and `unionDiscriminator` in `JsonSerializable` or adjust the +`unionRename` and `unionDiscriminator` in `ja:JsonSerializable` or adjust the default behavior by changing the corresponding fields in `build.yaml`. For more complex examples, please see [example]: From 3e79e24cca42db65d5d0fc542ae9a7039efd6256 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Tue, 8 Apr 2025 17:29:06 +0300 Subject: [PATCH 13/18] fix: add wip suffix to json_annotation version number --- example/pubspec.yaml | 2 +- json_annotation/CHANGELOG.md | 2 +- json_annotation/pubspec.yaml | 2 +- json_serializable/README.md | 18 +++++++++--------- json_serializable/pubspec.yaml | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index dc4791ea..9244fae6 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -7,7 +7,7 @@ environment: resolution: workspace dependencies: - json_annotation: ^4.10.0 + json_annotation: ^4.10.0-wip dev_dependencies: # Used by tests. Not required to use `json_serializable`. diff --git a/json_annotation/CHANGELOG.md b/json_annotation/CHANGELOG.md index f3c59b34..b0ac59e7 100644 --- a/json_annotation/CHANGELOG.md +++ b/json_annotation/CHANGELOG.md @@ -1,4 +1,4 @@ -## 4.10.0 +## 4.10.0-wip - Add `JsonSerializable.unionRename` - Add `JsonSerializable.unionDiscriminator` diff --git a/json_annotation/pubspec.yaml b/json_annotation/pubspec.yaml index 5a53ff2d..c2fe7842 100644 --- a/json_annotation/pubspec.yaml +++ b/json_annotation/pubspec.yaml @@ -1,5 +1,5 @@ name: json_annotation -version: 4.10.0 +version: 4.10.0-wip description: >- Classes and helper functions that support JSON code generation via the `json_serializable` package. diff --git a/json_serializable/README.md b/json_serializable/README.md index bf832ce9..7dcee747 100644 --- a/json_serializable/README.md +++ b/json_serializable/README.md @@ -356,15 +356,15 @@ targets: [`Enum`]: https://api.dart.dev/dart-core/Enum-class.html [`int`]: https://api.dart.dev/dart-core/int-class.html [`Iterable`]: https://api.dart.dev/dart-core/Iterable-class.html -[`JsonConverter`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonConverter-class.html -[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonEnum/valueField.html -[`JsonEnum`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonEnum-class.html -[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonKey/fromJson.html -[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonKey/toJson.html -[`JsonKey`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonKey-class.html -[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonLiteral-class.html -[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonSerializable-class.html -[`JsonValue`]: https://pub.dev/documentation/json_annotation/4.10.0/json_annotation/JsonValue-class.html +[`JsonConverter`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html +[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonEnum/valueField.html +[`JsonEnum`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonEnum-class.html +[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html +[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html +[`JsonKey`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey-class.html +[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonLiteral-class.html +[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable-class.html +[`JsonValue`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonValue-class.html [`List`]: https://api.dart.dev/dart-core/List-class.html [`Map`]: https://api.dart.dev/dart-core/Map-class.html [`num`]: https://api.dart.dev/dart-core/num-class.html diff --git a/json_serializable/pubspec.yaml b/json_serializable/pubspec.yaml index 343e1cda..6bb001a0 100644 --- a/json_serializable/pubspec.yaml +++ b/json_serializable/pubspec.yaml @@ -23,7 +23,7 @@ dependencies: # Use a tight version constraint to ensure that a constraint on # `json_annotation` properly constrains all features it provides. - json_annotation: '>=4.10.0 <4.11.0' + json_annotation: '>=4.10.0-wip <4.11.0' meta: ^1.14.0 path: ^1.9.0 pub_semver: ^2.1.4 From b62058d5d0d34ea15a720b91ae926efcbadcd5ac Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Fri, 13 Jun 2025 18:14:19 +0300 Subject: [PATCH 14/18] fix: fix formatting for new tests --- .../integration/sealed_class_examples.g.dart | 302 ++++---- .../src/conflicting_discriminator_input.dart | 4 +- .../test/src/missing_annotation_input.dart | 18 +- .../test/src/sealed_test_input.dart | 695 ++++++++---------- .../test/src/union_namer_input.dart | 187 ++--- 5 files changed, 566 insertions(+), 640 deletions(-) diff --git a/json_serializable/test/integration/sealed_class_examples.g.dart b/json_serializable/test/integration/sealed_class_examples.g.dart index cbc73561..598dbb8a 100644 --- a/json_serializable/test/integration/sealed_class_examples.g.dart +++ b/json_serializable/test/integration/sealed_class_examples.g.dart @@ -9,67 +9,57 @@ part of 'sealed_class_examples.dart'; // ************************************************************************** Vehicle _$VehicleFromJson(Map json) => switch (json['type']) { - 'Car' => _$CarFromJson(json), - 'Bicycle' => _$BicycleFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - Vehicle, - json, - ), - }; + 'Car' => _$CarFromJson(json), + 'Bicycle' => _$BicycleFromJson(json), + _ => throw UnrecognizedUnionTypeException('${json['type']}', Vehicle, json), +}; Map _$VehicleToJson(Vehicle instance) => switch (instance) { - final Car instance => { - 'type': 'Car', - ..._$CarToJson(instance), - }, - final Bicycle instance => { - 'type': 'Bicycle', - ..._$BicycleToJson(instance), - }, - }; + final Car instance => {'type': 'Car', ..._$CarToJson(instance)}, + final Bicycle instance => {'type': 'Bicycle', ..._$BicycleToJson(instance)}, +}; Car _$CarFromJson(Map json) => Car( - numberOfDoors: (json['numberOfDoors'] as num).toInt(), - vehicleID: json['vehicleID'] as String, - ); + numberOfDoors: (json['numberOfDoors'] as num).toInt(), + vehicleID: json['vehicleID'] as String, +); Map _$CarToJson(Car instance) => { - 'vehicleID': instance.vehicleID, - 'numberOfDoors': instance.numberOfDoors, - }; + 'vehicleID': instance.vehicleID, + 'numberOfDoors': instance.numberOfDoors, +}; Bicycle _$BicycleFromJson(Map json) => Bicycle( - hasBell: json['hasBell'] as bool, - vehicleID: json['vehicleID'] as String, - ); + hasBell: json['hasBell'] as bool, + vehicleID: json['vehicleID'] as String, +); Map _$BicycleToJson(Bicycle instance) => { - 'vehicleID': instance.vehicleID, - 'hasBell': instance.hasBell, - }; + 'vehicleID': instance.vehicleID, + 'hasBell': instance.hasBell, +}; Delivery _$DeliveryFromJson(Map json) => switch (json['delivery_type']) { 'drone_delivery' => _$DroneDeliveryFromJson(json), 'truck_delivery' => _$TruckDeliveryFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['delivery_type']}', - Delivery, - json, - ), + '${json['delivery_type']}', + Delivery, + json, + ), }; Map _$DeliveryToJson(Delivery instance) => switch (instance) { - final DroneDelivery instance => { - 'delivery_type': 'drone_delivery', - ..._$DroneDeliveryToJson(instance), - }, - final TruckDelivery instance => { - 'delivery_type': 'truck_delivery', - ..._$TruckDeliveryToJson(instance), - }, - }; + final DroneDelivery instance => { + 'delivery_type': 'drone_delivery', + ..._$DroneDeliveryToJson(instance), + }, + final TruckDelivery instance => { + 'delivery_type': 'truck_delivery', + ..._$TruckDeliveryToJson(instance), + }, +}; DroneDelivery _$DroneDeliveryFromJson(Map json) => DroneDelivery( @@ -99,71 +89,65 @@ Organization _$OrganizationFromJson(Map json) => switch (json['organization']) { 'Department' => _$DepartmentFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['organization']}', - Organization, - json, - ), + '${json['organization']}', + Organization, + json, + ), }; Map _$OrganizationToJson(Organization instance) => switch (instance) { final Department instance => { - 'organization': 'Department', - ..._$DepartmentToJson(instance), - }, + 'organization': 'Department', + ..._$DepartmentToJson(instance), + }, }; Department _$DepartmentFromJson(Map json) => switch (json['department']) { 'Team' => _$TeamFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['department']}', - Department, - json, - ), + '${json['department']}', + Department, + json, + ), }; Map _$DepartmentToJson(Department instance) => switch (instance) { - final Team instance => { - 'department': 'Team', - ..._$TeamToJson(instance), - }, + final Team instance => {'department': 'Team', ..._$TeamToJson(instance)}, }; Team _$TeamFromJson(Map json) => Team( - teamLead: json['teamLead'] as String, - departmentHead: json['departmentHead'] as String, - name: json['name'] as String, - ); + teamLead: json['teamLead'] as String, + departmentHead: json['departmentHead'] as String, + name: json['name'] as String, +); Map _$TeamToJson(Team instance) => { - 'name': instance.name, - 'departmentHead': instance.departmentHead, - 'teamLead': instance.teamLead, - }; + 'name': instance.name, + 'departmentHead': instance.departmentHead, + 'teamLead': instance.teamLead, +}; Transportable _$TransportableFromJson(Map json) => switch (json['type']) { 'Ship' => _$ShipFromJson(json), 'Package' => _$PackageFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - Transportable, - json, - ), + '${json['type']}', + Transportable, + json, + ), }; Map _$TransportableToJson(Transportable instance) => switch (instance) { - final Ship instance => { - 'type': 'Ship', - ..._$ShipToJson(instance), - }, + final Ship instance => {'type': 'Ship', ..._$ShipToJson(instance)}, final Package instance => { - 'type': 'Package', - ..._$PackageToJson(instance), - }, + 'type': 'Package', + ..._$PackageToJson(instance), + }, }; Trackable _$TrackableFromJson(Map json) => @@ -171,71 +155,63 @@ Trackable _$TrackableFromJson(Map json) => 'GPSDevice' => _$GPSDeviceFromJson(json), 'Package' => _$PackageFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - Trackable, - json, - ), + '${json['type']}', + Trackable, + json, + ), }; Map _$TrackableToJson(Trackable instance) => switch (instance) { final GPSDevice instance => { - 'type': 'GPSDevice', - ..._$GPSDeviceToJson(instance), - }, + 'type': 'GPSDevice', + ..._$GPSDeviceToJson(instance), + }, final Package instance => { - 'type': 'Package', - ..._$PackageToJson(instance), - }, + 'type': 'Package', + ..._$PackageToJson(instance), + }, }; -Ship _$ShipFromJson(Map json) => Ship( - cargoCapacity: (json['cargoCapacity'] as num).toDouble(), - ); +Ship _$ShipFromJson(Map json) => + Ship(cargoCapacity: (json['cargoCapacity'] as num).toDouble()); Map _$ShipToJson(Ship instance) => { - 'cargoCapacity': instance.cargoCapacity, - }; + 'cargoCapacity': instance.cargoCapacity, +}; -GPSDevice _$GPSDeviceFromJson(Map json) => GPSDevice( - serialNumber: json['serialNumber'] as String, - ); +GPSDevice _$GPSDeviceFromJson(Map json) => + GPSDevice(serialNumber: json['serialNumber'] as String); Map _$GPSDeviceToJson(GPSDevice instance) => { - 'serialNumber': instance.serialNumber, - }; + 'serialNumber': instance.serialNumber, +}; Package _$PackageFromJson(Map json) => Package( - label: json['label'] as String, - weight: (json['weight'] as num).toDouble(), - ); + label: json['label'] as String, + weight: (json['weight'] as num).toDouble(), +); Map _$PackageToJson(Package instance) => { - 'label': instance.label, - 'weight': instance.weight, - }; + 'label': instance.label, + 'weight': instance.weight, +}; Container _$ContainerFromJson(Map json) => switch (json['type']) { 'Box' => _$BoxFromJson(json), 'Parcel' => _$ParcelFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - Container, - json, - ), + '${json['type']}', + Container, + json, + ), }; Map _$ContainerToJson(Container instance) => switch (instance) { - final Box instance => { - 'type': 'Box', - ..._$BoxToJson(instance), - }, - final Parcel instance => { - 'type': 'Parcel', - ..._$ParcelToJson(instance), - }, + final Box instance => {'type': 'Box', ..._$BoxToJson(instance)}, + final Parcel instance => {'type': 'Parcel', ..._$ParcelToJson(instance)}, }; StorageItem _$StorageItemFromJson(Map json) => @@ -243,74 +219,76 @@ StorageItem _$StorageItemFromJson(Map json) => 'Product' => _$ProductFromJson(json), 'Equipment' => _$EquipmentFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - StorageItem, - json, - ), + '${json['type']}', + StorageItem, + json, + ), }; Map _$StorageItemToJson(StorageItem instance) => switch (instance) { final Product instance => { - 'type': 'Product', - ..._$ProductToJson(instance), - }, + 'type': 'Product', + ..._$ProductToJson(instance), + }, final Equipment instance => { - 'type': 'Equipment', - ..._$EquipmentToJson(instance), - }, + 'type': 'Equipment', + ..._$EquipmentToJson(instance), + }, }; Box _$BoxFromJson(Map json) => Box( - containerID: json['containerID'] as String, - items: (json['items'] as List?) - ?.map((e) => StorageItem.fromJson(e as Map)) - .toList() ?? - const [], - ); + containerID: json['containerID'] as String, + items: + (json['items'] as List?) + ?.map((e) => StorageItem.fromJson(e as Map)) + .toList() ?? + const [], +); Map _$BoxToJson(Box instance) => { - 'containerID': instance.containerID, - 'items': instance.items, - }; + 'containerID': instance.containerID, + 'items': instance.items, +}; Parcel _$ParcelFromJson(Map json) => Parcel( - containerID: json['containerID'] as String, - items: (json['items'] as List?) - ?.map((e) => StorageItem.fromJson(e as Map)) - .toList() ?? - const [], - ); + containerID: json['containerID'] as String, + items: + (json['items'] as List?) + ?.map((e) => StorageItem.fromJson(e as Map)) + .toList() ?? + const [], +); Map _$ParcelToJson(Parcel instance) => { - 'containerID': instance.containerID, - 'items': instance.items, - }; + 'containerID': instance.containerID, + 'items': instance.items, +}; Product _$ProductFromJson(Map json) => Product( - name: json['name'] as String, - itemID: json['itemID'] as String, - nestedContainer: json['nestedContainer'] == null - ? null - : Container.fromJson(json['nestedContainer'] as Map), - ); + name: json['name'] as String, + itemID: json['itemID'] as String, + nestedContainer: json['nestedContainer'] == null + ? null + : Container.fromJson(json['nestedContainer'] as Map), +); Map _$ProductToJson(Product instance) => { - 'itemID': instance.itemID, - 'nestedContainer': instance.nestedContainer, - 'name': instance.name, - }; + 'itemID': instance.itemID, + 'nestedContainer': instance.nestedContainer, + 'name': instance.name, +}; Equipment _$EquipmentFromJson(Map json) => Equipment( - label: json['label'] as String, - itemID: json['itemID'] as String, - nestedContainer: json['nestedContainer'] == null - ? null - : Container.fromJson(json['nestedContainer'] as Map), - ); + label: json['label'] as String, + itemID: json['itemID'] as String, + nestedContainer: json['nestedContainer'] == null + ? null + : Container.fromJson(json['nestedContainer'] as Map), +); Map _$EquipmentToJson(Equipment instance) => { - 'itemID': instance.itemID, - 'nestedContainer': instance.nestedContainer, - 'label': instance.label, - }; + 'itemID': instance.itemID, + 'nestedContainer': instance.nestedContainer, + 'label': instance.label, +}; diff --git a/json_serializable/test/src/conflicting_discriminator_input.dart b/json_serializable/test/src/conflicting_discriminator_input.dart index 67e3913d..fa2fc235 100644 --- a/json_serializable/test/src/conflicting_discriminator_input.dart +++ b/json_serializable/test/src/conflicting_discriminator_input.dart @@ -107,8 +107,8 @@ sealed class SuperSuperWithConflictingNestedDiscriminator @ShouldThrow( 'The classes `SuperSuperSuperWithConflictingNestedDiscriminator` and ' - '`SuperWithConflictingNestedDiscriminator` are nested sealed classes, but they have ' - 'the same discriminator `this_will_conflict`.', + '`SuperWithConflictingNestedDiscriminator` are nested sealed classes, ' + 'but they have the same discriminator `this_will_conflict`.', todo: 'Rename one of the discriminators with `unionDiscriminator` ' 'field of `@JsonSerializable`.', diff --git a/json_serializable/test/src/missing_annotation_input.dart b/json_serializable/test/src/missing_annotation_input.dart index d648e74c..ad5cd3ec 100644 --- a/json_serializable/test/src/missing_annotation_input.dart +++ b/json_serializable/test/src/missing_annotation_input.dart @@ -28,11 +28,12 @@ class SubWithoutSuperJsonSerializableAnnotationImpl implements SuperWithoutSuperJsonSerializableAnnotation {} @ShouldThrow( - 'The class `SuperWithSubExtWithoutJsonSerializableAnnotation` is sealed but its ' - 'subclass `SubWithoutJsonSerializableAnnotationExt` is not annotated with ' - '`JsonSerializable`.', + 'The class `SuperWithSubExtWithoutJsonSerializableAnnotation` is sealed but ' + 'its subclass `SubWithoutJsonSerializableAnnotationExt` is not annotated ' + 'with `JsonSerializable`.', todo: - 'Add `@JsonSerializable` annotation to SubWithoutJsonSerializableAnnotationExt.', + 'Add `@JsonSerializable` annotation to ' + 'SubWithoutJsonSerializableAnnotationExt.', element: 'SubWithoutJsonSerializableAnnotationExt', ) @JsonSerializable() @@ -42,11 +43,12 @@ class SubWithoutJsonSerializableAnnotationExt extends SuperWithSubExtWithoutJsonSerializableAnnotation {} @ShouldThrow( - 'The class `SuperWithSubImplWithoutJsonSerializableAnnotation` is sealed but its ' - 'subclass `SubWithoutJsonSerializableAnnotationImpl` is not annotated with ' - '`JsonSerializable`.', + 'The class `SuperWithSubImplWithoutJsonSerializableAnnotation` is sealed but ' + 'its subclass `SubWithoutJsonSerializableAnnotationImpl` is not annotated ' + 'with `JsonSerializable`.', todo: - 'Add `@JsonSerializable` annotation to SubWithoutJsonSerializableAnnotationImpl.', + 'Add `@JsonSerializable` annotation to ' + 'SubWithoutJsonSerializableAnnotationImpl.', element: 'SubWithoutJsonSerializableAnnotationImpl', ) @JsonSerializable() diff --git a/json_serializable/test/src/sealed_test_input.dart b/json_serializable/test/src/sealed_test_input.dart index 5afbca24..6711b0eb 100644 --- a/json_serializable/test/src/sealed_test_input.dart +++ b/json_serializable/test/src/sealed_test_input.dart @@ -4,29 +4,29 @@ part of '_json_serializable_test_input.dart'; @ShouldGenerate(r''' SuperSimpleSealedClass _$SuperSimpleSealedClassFromJson( - Map json) => - switch (json['type']) { - 'SubOneSimpleSealedClass' => _$SubOneSimpleSealedClassFromJson(json), - 'SubTwoSimpleSealedClass' => _$SubTwoSimpleSealedClassFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperSimpleSealedClass, - json, - ), - }; + Map json, +) => switch (json['type']) { + 'SubOneSimpleSealedClass' => _$SubOneSimpleSealedClassFromJson(json), + 'SubTwoSimpleSealedClass' => _$SubTwoSimpleSealedClassFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperSimpleSealedClass, + json, + ), +}; Map _$SuperSimpleSealedClassToJson( - SuperSimpleSealedClass instance) => - switch (instance) { - final SubOneSimpleSealedClass instance => { - 'type': 'SubOneSimpleSealedClass', - ..._$SubOneSimpleSealedClassToJson(instance), - }, - final SubTwoSimpleSealedClass instance => { - 'type': 'SubTwoSimpleSealedClass', - ..._$SubTwoSimpleSealedClassToJson(instance), - }, - }; + SuperSimpleSealedClass instance, +) => switch (instance) { + final SubOneSimpleSealedClass instance => { + 'type': 'SubOneSimpleSealedClass', + ..._$SubOneSimpleSealedClassToJson(instance), + }, + final SubTwoSimpleSealedClass instance => { + 'type': 'SubTwoSimpleSealedClass', + ..._$SubTwoSimpleSealedClassToJson(instance), + }, +}; ''') @JsonSerializable() sealed class SuperSimpleSealedClass { @@ -35,16 +35,12 @@ sealed class SuperSimpleSealedClass { @ShouldGenerate(r''' SubOneSimpleSealedClass _$SubOneSimpleSealedClassFromJson( - Map json) => - SubOneSimpleSealedClass( - someField: json['someField'] as String, - ); + Map json, +) => SubOneSimpleSealedClass(someField: json['someField'] as String); Map _$SubOneSimpleSealedClassToJson( - SubOneSimpleSealedClass instance) => - { - 'someField': instance.someField, - }; + SubOneSimpleSealedClass instance, +) => {'someField': instance.someField}; ''') @JsonSerializable() class SubOneSimpleSealedClass extends SuperSimpleSealedClass { @@ -55,16 +51,12 @@ class SubOneSimpleSealedClass extends SuperSimpleSealedClass { @ShouldGenerate(r''' SubTwoSimpleSealedClass _$SubTwoSimpleSealedClassFromJson( - Map json) => - SubTwoSimpleSealedClass( - someField: json['someField'] as String, - ); + Map json, +) => SubTwoSimpleSealedClass(someField: json['someField'] as String); Map _$SubTwoSimpleSealedClassToJson( - SubTwoSimpleSealedClass instance) => - { - 'someField': instance.someField, - }; + SubTwoSimpleSealedClass instance, +) => {'someField': instance.someField}; ''') @JsonSerializable() class SubTwoSimpleSealedClass extends SuperSimpleSealedClass { @@ -75,34 +67,32 @@ class SubTwoSimpleSealedClass extends SuperSimpleSealedClass { @ShouldGenerate(r''' SuperSimpleSealedClassWithChangedDiscriminator - _$SuperSimpleSealedClassWithChangedDiscriminatorFromJson( - Map json) => - switch (json['new_discriminator']) { - 'SubOneSimpleSealedClassWithChangedDiscriminator' => - _$SubOneSimpleSealedClassWithChangedDiscriminatorFromJson(json), - 'SubTwoSimpleSealedClassWithChangedDiscriminator' => - _$SubTwoSimpleSealedClassWithChangedDiscriminatorFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['new_discriminator']}', - SuperSimpleSealedClassWithChangedDiscriminator, - json, - ), - }; +_$SuperSimpleSealedClassWithChangedDiscriminatorFromJson( + Map json, +) => switch (json['new_discriminator']) { + 'SubOneSimpleSealedClassWithChangedDiscriminator' => + _$SubOneSimpleSealedClassWithChangedDiscriminatorFromJson(json), + 'SubTwoSimpleSealedClassWithChangedDiscriminator' => + _$SubTwoSimpleSealedClassWithChangedDiscriminatorFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['new_discriminator']}', + SuperSimpleSealedClassWithChangedDiscriminator, + json, + ), +}; Map _$SuperSimpleSealedClassWithChangedDiscriminatorToJson( - SuperSimpleSealedClassWithChangedDiscriminator instance) => - switch (instance) { - final SubOneSimpleSealedClassWithChangedDiscriminator instance => { - 'new_discriminator': - 'SubOneSimpleSealedClassWithChangedDiscriminator', - ..._$SubOneSimpleSealedClassWithChangedDiscriminatorToJson(instance), - }, - final SubTwoSimpleSealedClassWithChangedDiscriminator instance => { - 'new_discriminator': - 'SubTwoSimpleSealedClassWithChangedDiscriminator', - ..._$SubTwoSimpleSealedClassWithChangedDiscriminatorToJson(instance), - }, - }; + SuperSimpleSealedClassWithChangedDiscriminator instance, +) => switch (instance) { + final SubOneSimpleSealedClassWithChangedDiscriminator instance => { + 'new_discriminator': 'SubOneSimpleSealedClassWithChangedDiscriminator', + ..._$SubOneSimpleSealedClassWithChangedDiscriminatorToJson(instance), + }, + final SubTwoSimpleSealedClassWithChangedDiscriminator instance => { + 'new_discriminator': 'SubTwoSimpleSealedClassWithChangedDiscriminator', + ..._$SubTwoSimpleSealedClassWithChangedDiscriminatorToJson(instance), + }, +}; ''') @JsonSerializable(unionDiscriminator: 'new_discriminator') sealed class SuperSimpleSealedClassWithChangedDiscriminator { @@ -111,17 +101,15 @@ sealed class SuperSimpleSealedClassWithChangedDiscriminator { @ShouldGenerate(r''' SubOneSimpleSealedClassWithChangedDiscriminator - _$SubOneSimpleSealedClassWithChangedDiscriminatorFromJson( - Map json) => - SubOneSimpleSealedClassWithChangedDiscriminator( - someField: json['someField'] as String, - ); +_$SubOneSimpleSealedClassWithChangedDiscriminatorFromJson( + Map json, +) => SubOneSimpleSealedClassWithChangedDiscriminator( + someField: json['someField'] as String, +); Map _$SubOneSimpleSealedClassWithChangedDiscriminatorToJson( - SubOneSimpleSealedClassWithChangedDiscriminator instance) => - { - 'someField': instance.someField, - }; + SubOneSimpleSealedClassWithChangedDiscriminator instance, +) => {'someField': instance.someField}; ''') @JsonSerializable() class SubOneSimpleSealedClassWithChangedDiscriminator @@ -133,17 +121,15 @@ class SubOneSimpleSealedClassWithChangedDiscriminator @ShouldGenerate(r''' SubTwoSimpleSealedClassWithChangedDiscriminator - _$SubTwoSimpleSealedClassWithChangedDiscriminatorFromJson( - Map json) => - SubTwoSimpleSealedClassWithChangedDiscriminator( - someField: json['someField'] as String, - ); +_$SubTwoSimpleSealedClassWithChangedDiscriminatorFromJson( + Map json, +) => SubTwoSimpleSealedClassWithChangedDiscriminator( + someField: json['someField'] as String, +); Map _$SubTwoSimpleSealedClassWithChangedDiscriminatorToJson( - SubTwoSimpleSealedClassWithChangedDiscriminator instance) => - { - 'someField': instance.someField, - }; + SubTwoSimpleSealedClassWithChangedDiscriminator instance, +) => {'someField': instance.someField}; ''') @JsonSerializable() class SubTwoSimpleSealedClassWithChangedDiscriminator @@ -155,32 +141,32 @@ class SubTwoSimpleSealedClassWithChangedDiscriminator @ShouldGenerate(r''' SuperSimpleSealedClassWithChangedUnionRename - _$SuperSimpleSealedClassWithChangedUnionRenameFromJson( - Map json) => - switch (json['type']) { - 'sub_one_simple_sealed_class_with_changed_union_rename' => - _$SubOneSimpleSealedClassWithChangedUnionRenameFromJson(json), - 'sub_two_simple_sealed_class_with_changed_union_rename' => - _$SubTwoSimpleSealedClassWithChangedUnionRenameFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperSimpleSealedClassWithChangedUnionRename, - json, - ), - }; +_$SuperSimpleSealedClassWithChangedUnionRenameFromJson( + Map json, +) => switch (json['type']) { + 'sub_one_simple_sealed_class_with_changed_union_rename' => + _$SubOneSimpleSealedClassWithChangedUnionRenameFromJson(json), + 'sub_two_simple_sealed_class_with_changed_union_rename' => + _$SubTwoSimpleSealedClassWithChangedUnionRenameFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperSimpleSealedClassWithChangedUnionRename, + json, + ), +}; Map _$SuperSimpleSealedClassWithChangedUnionRenameToJson( - SuperSimpleSealedClassWithChangedUnionRename instance) => - switch (instance) { - final SubOneSimpleSealedClassWithChangedUnionRename instance => { - 'type': 'sub_one_simple_sealed_class_with_changed_union_rename', - ..._$SubOneSimpleSealedClassWithChangedUnionRenameToJson(instance), - }, - final SubTwoSimpleSealedClassWithChangedUnionRename instance => { - 'type': 'sub_two_simple_sealed_class_with_changed_union_rename', - ..._$SubTwoSimpleSealedClassWithChangedUnionRenameToJson(instance), - }, - }; + SuperSimpleSealedClassWithChangedUnionRename instance, +) => switch (instance) { + final SubOneSimpleSealedClassWithChangedUnionRename instance => { + 'type': 'sub_one_simple_sealed_class_with_changed_union_rename', + ..._$SubOneSimpleSealedClassWithChangedUnionRenameToJson(instance), + }, + final SubTwoSimpleSealedClassWithChangedUnionRename instance => { + 'type': 'sub_two_simple_sealed_class_with_changed_union_rename', + ..._$SubTwoSimpleSealedClassWithChangedUnionRenameToJson(instance), + }, +}; ''') @JsonSerializable(unionRename: UnionRename.snake) sealed class SuperSimpleSealedClassWithChangedUnionRename { @@ -189,17 +175,15 @@ sealed class SuperSimpleSealedClassWithChangedUnionRename { @ShouldGenerate(r''' SubOneSimpleSealedClassWithChangedUnionRename - _$SubOneSimpleSealedClassWithChangedUnionRenameFromJson( - Map json) => - SubOneSimpleSealedClassWithChangedUnionRename( - someField: json['someField'] as String, - ); +_$SubOneSimpleSealedClassWithChangedUnionRenameFromJson( + Map json, +) => SubOneSimpleSealedClassWithChangedUnionRename( + someField: json['someField'] as String, +); Map _$SubOneSimpleSealedClassWithChangedUnionRenameToJson( - SubOneSimpleSealedClassWithChangedUnionRename instance) => - { - 'someField': instance.someField, - }; + SubOneSimpleSealedClassWithChangedUnionRename instance, +) => {'someField': instance.someField}; ''') @JsonSerializable() class SubOneSimpleSealedClassWithChangedUnionRename @@ -211,17 +195,15 @@ class SubOneSimpleSealedClassWithChangedUnionRename @ShouldGenerate(r''' SubTwoSimpleSealedClassWithChangedUnionRename - _$SubTwoSimpleSealedClassWithChangedUnionRenameFromJson( - Map json) => - SubTwoSimpleSealedClassWithChangedUnionRename( - someField: json['someField'] as String, - ); +_$SubTwoSimpleSealedClassWithChangedUnionRenameFromJson( + Map json, +) => SubTwoSimpleSealedClassWithChangedUnionRename( + someField: json['someField'] as String, +); Map _$SubTwoSimpleSealedClassWithChangedUnionRenameToJson( - SubTwoSimpleSealedClassWithChangedUnionRename instance) => - { - 'someField': instance.someField, - }; + SubTwoSimpleSealedClassWithChangedUnionRename instance, +) => {'someField': instance.someField}; ''') @JsonSerializable() class SubTwoSimpleSealedClassWithChangedUnionRename @@ -233,44 +215,47 @@ class SubTwoSimpleSealedClassWithChangedUnionRename @ShouldGenerate(r''' SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename - _$SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( - Map json) => - switch (json['my_discriminator']) { - 'sub-one-simple-sealed-class-with-changed-discriminator-and-union-rename' => - _$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( - json), - 'sub-two-simple-sealed-class-with-changed-discriminator-and-union-rename' => - _$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( - json), - _ => throw UnrecognizedUnionTypeException( - '${json['my_discriminator']}', - SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename, - json, - ), - }; +_$SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + Map json, +) => switch (json['my_discriminator']) { + 'sub-one-simple-sealed-class-with-changed-discriminator-and-union-rename' => + _$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + json, + ), + 'sub-two-simple-sealed-class-with-changed-discriminator-and-union-rename' => + _$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + json, + ), + _ => throw UnrecognizedUnionTypeException( + '${json['my_discriminator']}', + SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename, + json, + ), +}; Map - _$SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( - SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename - instance) => - switch (instance) { - final SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename - instance => - { - 'my_discriminator': - 'sub-one-simple-sealed-class-with-changed-discriminator-and-union-rename', - ..._$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( - instance), - }, - final SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename - instance => - { - 'my_discriminator': - 'sub-two-simple-sealed-class-with-changed-discriminator-and-union-rename', - ..._$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( - instance), - }, - }; +_$SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename instance, +) => switch (instance) { + final SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename + instance => + { + 'my_discriminator': + 'sub-one-simple-sealed-class-with-changed-discriminator-and-union-rename', + ..._$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + instance, + ), + }, + final SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename + instance => + { + 'my_discriminator': + 'sub-two-simple-sealed-class-with-changed-discriminator-and-union-rename', + ..._$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + instance, + ), + }, +}; ''') @JsonSerializable( unionDiscriminator: 'my_discriminator', @@ -282,19 +267,16 @@ sealed class SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename { @ShouldGenerate(r''' SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename - _$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( - Map json) => - SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename( - someField: json['someField'] as String, - ); +_$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + Map json, +) => SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename( + someField: json['someField'] as String, +); Map - _$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( - SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename - instance) => - { - 'someField': instance.someField, - }; +_$SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename instance, +) => {'someField': instance.someField}; ''') @JsonSerializable() class SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename @@ -308,19 +290,16 @@ class SubOneSimpleSealedClassWithChangedDiscriminatorAndUnionRename @ShouldGenerate(r''' SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename - _$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( - Map json) => - SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename( - someField: json['someField'] as String, - ); +_$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameFromJson( + Map json, +) => SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename( + someField: json['someField'] as String, +); Map - _$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( - SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename - instance) => - { - 'someField': instance.someField, - }; +_$SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( + SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename instance, +) => {'someField': instance.someField}; ''') @JsonSerializable() class SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename @@ -334,24 +313,22 @@ class SubTwoSimpleSealedClassWithChangedDiscriminatorAndUnionRename @ShouldGenerate(r''' SuperSimpleSealedClassWithoutToJson - _$SuperSimpleSealedClassWithoutToJsonFromJson(Map json) => - switch (json['type']) { - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperSimpleSealedClassWithoutToJson, - json, - ), - }; +_$SuperSimpleSealedClassWithoutToJsonFromJson(Map json) => + switch (json['type']) { + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperSimpleSealedClassWithoutToJson, + json, + ), + }; ''') @JsonSerializable(createToJson: false) sealed class SuperSimpleSealedClassWithoutToJson {} @ShouldGenerate(r''' SubSimpleSealedClassWithoutToJson _$SubSimpleSealedClassWithoutToJsonFromJson( - Map json) => - SubSimpleSealedClassWithoutToJson( - someField: json['someField'] as String, - ); + Map json, +) => SubSimpleSealedClassWithoutToJson(someField: json['someField'] as String); ''') @JsonSerializable(createToJson: false) class SubSimpleSealedClassWithoutToJson { @@ -362,29 +339,29 @@ class SubSimpleSealedClassWithoutToJson { @ShouldGenerate(r''' SuperSuperSuperNested _$SuperSuperSuperNestedFromJson( - Map json) => - switch (json['super_super_super_type']) { - 'SuperSuperNestedOne' => _$SuperSuperNestedOneFromJson(json), - 'SuperSuperNestedTwo' => _$SuperSuperNestedTwoFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['super_super_super_type']}', - SuperSuperSuperNested, - json, - ), - }; + Map json, +) => switch (json['super_super_super_type']) { + 'SuperSuperNestedOne' => _$SuperSuperNestedOneFromJson(json), + 'SuperSuperNestedTwo' => _$SuperSuperNestedTwoFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['super_super_super_type']}', + SuperSuperSuperNested, + json, + ), +}; Map _$SuperSuperSuperNestedToJson( - SuperSuperSuperNested instance) => - switch (instance) { - final SuperSuperNestedOne instance => { - 'super_super_super_type': 'SuperSuperNestedOne', - ..._$SuperSuperNestedOneToJson(instance), - }, - final SuperSuperNestedTwo instance => { - 'super_super_super_type': 'SuperSuperNestedTwo', - ..._$SuperSuperNestedTwoToJson(instance), - }, - }; + SuperSuperSuperNested instance, +) => switch (instance) { + final SuperSuperNestedOne instance => { + 'super_super_super_type': 'SuperSuperNestedOne', + ..._$SuperSuperNestedOneToJson(instance), + }, + final SuperSuperNestedTwo instance => { + 'super_super_super_type': 'SuperSuperNestedTwo', + ..._$SuperSuperNestedTwoToJson(instance), + }, +}; ''') @JsonSerializable(unionDiscriminator: 'super_super_super_type') sealed class SuperSuperSuperNested { @@ -397,24 +374,24 @@ SuperSuperNestedOne _$SuperSuperNestedOneFromJson(Map json) => 'SuperNestedOneOne' => _$SuperNestedOneOneFromJson(json), 'SuperNestedOneTwo' => _$SuperNestedOneTwoFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['super_super_type']}', - SuperSuperNestedOne, - json, - ), + '${json['super_super_type']}', + SuperSuperNestedOne, + json, + ), }; Map _$SuperSuperNestedOneToJson( - SuperSuperNestedOne instance) => - switch (instance) { - final SuperNestedOneOne instance => { - 'super_super_type': 'SuperNestedOneOne', - ..._$SuperNestedOneOneToJson(instance), - }, - final SuperNestedOneTwo instance => { - 'super_super_type': 'SuperNestedOneTwo', - ..._$SuperNestedOneTwoToJson(instance), - }, - }; + SuperSuperNestedOne instance, +) => switch (instance) { + final SuperNestedOneOne instance => { + 'super_super_type': 'SuperNestedOneOne', + ..._$SuperNestedOneOneToJson(instance), + }, + final SuperNestedOneTwo instance => { + 'super_super_type': 'SuperNestedOneTwo', + ..._$SuperNestedOneTwoToJson(instance), + }, +}; ''') @JsonSerializable(unionDiscriminator: 'super_super_type') sealed class SuperSuperNestedOne extends SuperSuperSuperNested { @@ -427,24 +404,24 @@ SuperSuperNestedTwo _$SuperSuperNestedTwoFromJson(Map json) => 'SuperNestedTwoOne' => _$SuperNestedTwoOneFromJson(json), 'SuperNestedTwoTwo' => _$SuperNestedTwoTwoFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['super_super_type']}', - SuperSuperNestedTwo, - json, - ), + '${json['super_super_type']}', + SuperSuperNestedTwo, + json, + ), }; Map _$SuperSuperNestedTwoToJson( - SuperSuperNestedTwo instance) => - switch (instance) { - final SuperNestedTwoOne instance => { - 'super_super_type': 'SuperNestedTwoOne', - ..._$SuperNestedTwoOneToJson(instance), - }, - final SuperNestedTwoTwo instance => { - 'super_super_type': 'SuperNestedTwoTwo', - ..._$SuperNestedTwoTwoToJson(instance), - }, - }; + SuperSuperNestedTwo instance, +) => switch (instance) { + final SuperNestedTwoOne instance => { + 'super_super_type': 'SuperNestedTwoOne', + ..._$SuperNestedTwoOneToJson(instance), + }, + final SuperNestedTwoTwo instance => { + 'super_super_type': 'SuperNestedTwoTwo', + ..._$SuperNestedTwoTwoToJson(instance), + }, +}; ''') @JsonSerializable(unionDiscriminator: 'super_super_type') sealed class SuperSuperNestedTwo extends SuperSuperSuperNested { @@ -456,18 +433,18 @@ SuperNestedOneOne _$SuperNestedOneOneFromJson(Map json) => switch (json['super_type']) { 'SubNestedOneOne' => _$SubNestedOneOneFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['super_type']}', - SuperNestedOneOne, - json, - ), + '${json['super_type']}', + SuperNestedOneOne, + json, + ), }; Map _$SuperNestedOneOneToJson(SuperNestedOneOne instance) => switch (instance) { final SubNestedOneOne instance => { - 'super_type': 'SubNestedOneOne', - ..._$SubNestedOneOneToJson(instance), - }, + 'super_type': 'SubNestedOneOne', + ..._$SubNestedOneOneToJson(instance), + }, }; ''') @JsonSerializable(unionDiscriminator: 'super_type') @@ -480,18 +457,18 @@ SuperNestedOneTwo _$SuperNestedOneTwoFromJson(Map json) => switch (json['super_type']) { 'SubNestedOneTwo' => _$SubNestedOneTwoFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['super_type']}', - SuperNestedOneTwo, - json, - ), + '${json['super_type']}', + SuperNestedOneTwo, + json, + ), }; Map _$SuperNestedOneTwoToJson(SuperNestedOneTwo instance) => switch (instance) { final SubNestedOneTwo instance => { - 'super_type': 'SubNestedOneTwo', - ..._$SubNestedOneTwoToJson(instance), - }, + 'super_type': 'SubNestedOneTwo', + ..._$SubNestedOneTwoToJson(instance), + }, }; ''') @JsonSerializable(unionDiscriminator: 'super_type') @@ -504,18 +481,18 @@ SuperNestedTwoOne _$SuperNestedTwoOneFromJson(Map json) => switch (json['super_type']) { 'SubNestedTwoOne' => _$SubNestedTwoOneFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['super_type']}', - SuperNestedTwoOne, - json, - ), + '${json['super_type']}', + SuperNestedTwoOne, + json, + ), }; Map _$SuperNestedTwoOneToJson(SuperNestedTwoOne instance) => switch (instance) { final SubNestedTwoOne instance => { - 'super_type': 'SubNestedTwoOne', - ..._$SubNestedTwoOneToJson(instance), - }, + 'super_type': 'SubNestedTwoOne', + ..._$SubNestedTwoOneToJson(instance), + }, }; ''') @JsonSerializable(unionDiscriminator: 'super_type') @@ -528,18 +505,18 @@ SuperNestedTwoTwo _$SuperNestedTwoTwoFromJson(Map json) => switch (json['super_type']) { 'SubNestedTwoTwo' => _$SubNestedTwoTwoFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['super_type']}', - SuperNestedTwoTwo, - json, - ), + '${json['super_type']}', + SuperNestedTwoTwo, + json, + ), }; Map _$SuperNestedTwoTwoToJson(SuperNestedTwoTwo instance) => switch (instance) { final SubNestedTwoTwo instance => { - 'super_type': 'SubNestedTwoTwo', - ..._$SubNestedTwoTwoToJson(instance), - }, + 'super_type': 'SubNestedTwoTwo', + ..._$SubNestedTwoTwoToJson(instance), + }, }; ''') @JsonSerializable(unionDiscriminator: 'super_type') @@ -549,14 +526,10 @@ sealed class SuperNestedTwoTwo extends SuperSuperNestedTwo { @ShouldGenerate(r''' SubNestedOneOne _$SubNestedOneOneFromJson(Map json) => - SubNestedOneOne( - oneOneField: json['oneOneField'] as String, - ); + SubNestedOneOne(oneOneField: json['oneOneField'] as String); Map _$SubNestedOneOneToJson(SubNestedOneOne instance) => - { - 'oneOneField': instance.oneOneField, - }; + {'oneOneField': instance.oneOneField}; ''') @JsonSerializable() class SubNestedOneOne extends SuperNestedOneOne { @@ -567,14 +540,10 @@ class SubNestedOneOne extends SuperNestedOneOne { @ShouldGenerate(r''' SubNestedOneTwo _$SubNestedOneTwoFromJson(Map json) => - SubNestedOneTwo( - oneTwoField: json['oneTwoField'] as String, - ); + SubNestedOneTwo(oneTwoField: json['oneTwoField'] as String); Map _$SubNestedOneTwoToJson(SubNestedOneTwo instance) => - { - 'oneTwoField': instance.oneTwoField, - }; + {'oneTwoField': instance.oneTwoField}; ''') @JsonSerializable() class SubNestedOneTwo extends SuperNestedOneTwo { @@ -585,14 +554,10 @@ class SubNestedOneTwo extends SuperNestedOneTwo { @ShouldGenerate(r''' SubNestedTwoOne _$SubNestedTwoOneFromJson(Map json) => - SubNestedTwoOne( - twoOneField: json['twoOneField'] as String, - ); + SubNestedTwoOne(twoOneField: json['twoOneField'] as String); Map _$SubNestedTwoOneToJson(SubNestedTwoOne instance) => - { - 'twoOneField': instance.twoOneField, - }; + {'twoOneField': instance.twoOneField}; ''') @JsonSerializable() class SubNestedTwoOne extends SuperNestedTwoOne { @@ -603,14 +568,10 @@ class SubNestedTwoOne extends SuperNestedTwoOne { @ShouldGenerate(r''' SubNestedTwoTwo _$SubNestedTwoTwoFromJson(Map json) => - SubNestedTwoTwo( - twoTwoField: json['twoTwoField'] as String, - ); + SubNestedTwoTwo(twoTwoField: json['twoTwoField'] as String); Map _$SubNestedTwoTwoToJson(SubNestedTwoTwo instance) => - { - 'twoTwoField': instance.twoTwoField, - }; + {'twoTwoField': instance.twoTwoField}; ''') @JsonSerializable() class SubNestedTwoTwo extends SuperNestedTwoTwo { @@ -621,82 +582,78 @@ class SubNestedTwoTwo extends SuperNestedTwoTwo { @ShouldGenerate(r''' SuperMultipleImplOne _$SuperMultipleImplOneFromJson( - Map json) => - switch (json['type']) { - 'SubOneMultipleImpl' => _$SubOneMultipleImplFromJson(json), - 'SubThreeMultipleImpl' => _$SubThreeMultipleImplFromJson(json), - 'SubFourMultipleImpl' => _$SubFourMultipleImplFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperMultipleImplOne, - json, - ), - }; + Map json, +) => switch (json['type']) { + 'SubOneMultipleImpl' => _$SubOneMultipleImplFromJson(json), + 'SubThreeMultipleImpl' => _$SubThreeMultipleImplFromJson(json), + 'SubFourMultipleImpl' => _$SubFourMultipleImplFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperMultipleImplOne, + json, + ), +}; Map _$SuperMultipleImplOneToJson( - SuperMultipleImplOne instance) => - switch (instance) { - final SubOneMultipleImpl instance => { - 'type': 'SubOneMultipleImpl', - ..._$SubOneMultipleImplToJson(instance), - }, - final SubThreeMultipleImpl instance => { - 'type': 'SubThreeMultipleImpl', - ..._$SubThreeMultipleImplToJson(instance), - }, - final SubFourMultipleImpl instance => { - 'type': 'SubFourMultipleImpl', - ..._$SubFourMultipleImplToJson(instance), - }, - }; + SuperMultipleImplOne instance, +) => switch (instance) { + final SubOneMultipleImpl instance => { + 'type': 'SubOneMultipleImpl', + ..._$SubOneMultipleImplToJson(instance), + }, + final SubThreeMultipleImpl instance => { + 'type': 'SubThreeMultipleImpl', + ..._$SubThreeMultipleImplToJson(instance), + }, + final SubFourMultipleImpl instance => { + 'type': 'SubFourMultipleImpl', + ..._$SubFourMultipleImplToJson(instance), + }, +}; ''') @JsonSerializable() sealed class SuperMultipleImplOne {} @ShouldGenerate(r''' SuperMultipleImplTwo _$SuperMultipleImplTwoFromJson( - Map json) => - switch (json['type']) { - 'SubTwoMultipleImpl' => _$SubTwoMultipleImplFromJson(json), - 'SubThreeMultipleImpl' => _$SubThreeMultipleImplFromJson(json), - 'SubFourMultipleImpl' => _$SubFourMultipleImplFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperMultipleImplTwo, - json, - ), - }; + Map json, +) => switch (json['type']) { + 'SubTwoMultipleImpl' => _$SubTwoMultipleImplFromJson(json), + 'SubThreeMultipleImpl' => _$SubThreeMultipleImplFromJson(json), + 'SubFourMultipleImpl' => _$SubFourMultipleImplFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperMultipleImplTwo, + json, + ), +}; Map _$SuperMultipleImplTwoToJson( - SuperMultipleImplTwo instance) => - switch (instance) { - final SubTwoMultipleImpl instance => { - 'type': 'SubTwoMultipleImpl', - ..._$SubTwoMultipleImplToJson(instance), - }, - final SubThreeMultipleImpl instance => { - 'type': 'SubThreeMultipleImpl', - ..._$SubThreeMultipleImplToJson(instance), - }, - final SubFourMultipleImpl instance => { - 'type': 'SubFourMultipleImpl', - ..._$SubFourMultipleImplToJson(instance), - }, - }; + SuperMultipleImplTwo instance, +) => switch (instance) { + final SubTwoMultipleImpl instance => { + 'type': 'SubTwoMultipleImpl', + ..._$SubTwoMultipleImplToJson(instance), + }, + final SubThreeMultipleImpl instance => { + 'type': 'SubThreeMultipleImpl', + ..._$SubThreeMultipleImplToJson(instance), + }, + final SubFourMultipleImpl instance => { + 'type': 'SubFourMultipleImpl', + ..._$SubFourMultipleImplToJson(instance), + }, +}; ''') @JsonSerializable() sealed class SuperMultipleImplTwo {} @ShouldGenerate(r''' SubOneMultipleImpl _$SubOneMultipleImplFromJson(Map json) => - SubOneMultipleImpl( - json['subOneField'] as String, - ); + SubOneMultipleImpl(json['subOneField'] as String); Map _$SubOneMultipleImplToJson(SubOneMultipleImpl instance) => - { - 'subOneField': instance.subOneField, - }; + {'subOneField': instance.subOneField}; ''') @JsonSerializable() class SubOneMultipleImpl implements SuperMultipleImplOne { @@ -707,14 +664,10 @@ class SubOneMultipleImpl implements SuperMultipleImplOne { @ShouldGenerate(r''' SubTwoMultipleImpl _$SubTwoMultipleImplFromJson(Map json) => - SubTwoMultipleImpl( - json['subTwoField'] as String, - ); + SubTwoMultipleImpl(json['subTwoField'] as String); Map _$SubTwoMultipleImplToJson(SubTwoMultipleImpl instance) => - { - 'subTwoField': instance.subTwoField, - }; + {'subTwoField': instance.subTwoField}; ''') @JsonSerializable() class SubTwoMultipleImpl implements SuperMultipleImplTwo { @@ -725,16 +678,12 @@ class SubTwoMultipleImpl implements SuperMultipleImplTwo { @ShouldGenerate(r''' SubThreeMultipleImpl _$SubThreeMultipleImplFromJson( - Map json) => - SubThreeMultipleImpl( - json['subThreeField'] as String, - ); + Map json, +) => SubThreeMultipleImpl(json['subThreeField'] as String); Map _$SubThreeMultipleImplToJson( - SubThreeMultipleImpl instance) => - { - 'subThreeField': instance.subThreeField, - }; + SubThreeMultipleImpl instance, +) => {'subThreeField': instance.subThreeField}; ''') @JsonSerializable() class SubThreeMultipleImpl @@ -746,15 +695,11 @@ class SubThreeMultipleImpl @ShouldGenerate(r''' SubFourMultipleImpl _$SubFourMultipleImplFromJson(Map json) => - SubFourMultipleImpl( - json['subFourField'] as String, - ); + SubFourMultipleImpl(json['subFourField'] as String); Map _$SubFourMultipleImplToJson( - SubFourMultipleImpl instance) => - { - 'subFourField': instance.subFourField, - }; + SubFourMultipleImpl instance, +) => {'subFourField': instance.subFourField}; ''') @JsonSerializable() class SubFourMultipleImpl diff --git a/json_serializable/test/src/union_namer_input.dart b/json_serializable/test/src/union_namer_input.dart index a1225e27..80b7734c 100644 --- a/json_serializable/test/src/union_namer_input.dart +++ b/json_serializable/test/src/union_namer_input.dart @@ -4,24 +4,24 @@ part of '_json_serializable_test_input.dart'; @ShouldGenerate(r''' SuperUnionRenameNone _$SuperUnionRenameNoneFromJson( - Map json) => - switch (json['type']) { - 'SubUnionRenameNone' => _$SubUnionRenameNoneFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperUnionRenameNone, - json, - ), - }; + Map json, +) => switch (json['type']) { + 'SubUnionRenameNone' => _$SubUnionRenameNoneFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenameNone, + json, + ), +}; Map _$SuperUnionRenameNoneToJson( - SuperUnionRenameNone instance) => - switch (instance) { - final SubUnionRenameNone instance => { - 'type': 'SubUnionRenameNone', - ..._$SubUnionRenameNoneToJson(instance), - }, - }; + SuperUnionRenameNone instance, +) => switch (instance) { + final SubUnionRenameNone instance => { + 'type': 'SubUnionRenameNone', + ..._$SubUnionRenameNoneToJson(instance), + }, +}; ''') @JsonSerializable(unionRename: UnionRename.none) sealed class SuperUnionRenameNone {} @@ -38,24 +38,24 @@ class SubUnionRenameNone extends SuperUnionRenameNone {} @ShouldGenerate(r''' SuperUnionRenameKebab _$SuperUnionRenameKebabFromJson( - Map json) => - switch (json['type']) { - 'sub-union-rename-kebab' => _$SubUnionRenameKebabFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperUnionRenameKebab, - json, - ), - }; + Map json, +) => switch (json['type']) { + 'sub-union-rename-kebab' => _$SubUnionRenameKebabFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenameKebab, + json, + ), +}; Map _$SuperUnionRenameKebabToJson( - SuperUnionRenameKebab instance) => - switch (instance) { - final SubUnionRenameKebab instance => { - 'type': 'sub-union-rename-kebab', - ..._$SubUnionRenameKebabToJson(instance), - }, - }; + SuperUnionRenameKebab instance, +) => switch (instance) { + final SubUnionRenameKebab instance => { + 'type': 'sub-union-rename-kebab', + ..._$SubUnionRenameKebabToJson(instance), + }, +}; ''') @JsonSerializable(unionRename: UnionRename.kebab) sealed class SuperUnionRenameKebab {} @@ -65,32 +65,32 @@ SubUnionRenameKebab _$SubUnionRenameKebabFromJson(Map json) => SubUnionRenameKebab(); Map _$SubUnionRenameKebabToJson( - SubUnionRenameKebab instance) => - {}; + SubUnionRenameKebab instance, +) => {}; ''') @JsonSerializable() class SubUnionRenameKebab extends SuperUnionRenameKebab {} @ShouldGenerate(r''' SuperUnionRenameSnake _$SuperUnionRenameSnakeFromJson( - Map json) => - switch (json['type']) { - 'sub_union_rename_snake' => _$SubUnionRenameSnakeFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperUnionRenameSnake, - json, - ), - }; + Map json, +) => switch (json['type']) { + 'sub_union_rename_snake' => _$SubUnionRenameSnakeFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenameSnake, + json, + ), +}; Map _$SuperUnionRenameSnakeToJson( - SuperUnionRenameSnake instance) => - switch (instance) { - final SubUnionRenameSnake instance => { - 'type': 'sub_union_rename_snake', - ..._$SubUnionRenameSnakeToJson(instance), - }, - }; + SuperUnionRenameSnake instance, +) => switch (instance) { + final SubUnionRenameSnake instance => { + 'type': 'sub_union_rename_snake', + ..._$SubUnionRenameSnakeToJson(instance), + }, +}; ''') @JsonSerializable(unionRename: UnionRename.snake) sealed class SuperUnionRenameSnake {} @@ -100,81 +100,82 @@ SubUnionRenameSnake _$SubUnionRenameSnakeFromJson(Map json) => SubUnionRenameSnake(); Map _$SubUnionRenameSnakeToJson( - SubUnionRenameSnake instance) => - {}; + SubUnionRenameSnake instance, +) => {}; ''') @JsonSerializable() class SubUnionRenameSnake extends SuperUnionRenameSnake {} @ShouldGenerate(r''' SuperUnionRenamePascal _$SuperUnionRenamePascalFromJson( - Map json) => - switch (json['type']) { - 'SubUnionRenamePascal' => _$SubUnionRenamePascalFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperUnionRenamePascal, - json, - ), - }; + Map json, +) => switch (json['type']) { + 'SubUnionRenamePascal' => _$SubUnionRenamePascalFromJson(json), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenamePascal, + json, + ), +}; Map _$SuperUnionRenamePascalToJson( - SuperUnionRenamePascal instance) => - switch (instance) { - final SubUnionRenamePascal instance => { - 'type': 'SubUnionRenamePascal', - ..._$SubUnionRenamePascalToJson(instance), - }, - }; + SuperUnionRenamePascal instance, +) => switch (instance) { + final SubUnionRenamePascal instance => { + 'type': 'SubUnionRenamePascal', + ..._$SubUnionRenamePascalToJson(instance), + }, +}; ''') @JsonSerializable(unionRename: UnionRename.pascal) sealed class SuperUnionRenamePascal {} @ShouldGenerate(r''' SubUnionRenamePascal _$SubUnionRenamePascalFromJson( - Map json) => - SubUnionRenamePascal(); + Map json, +) => SubUnionRenamePascal(); Map _$SubUnionRenamePascalToJson( - SubUnionRenamePascal instance) => - {}; + SubUnionRenamePascal instance, +) => {}; ''') @JsonSerializable() class SubUnionRenamePascal extends SuperUnionRenamePascal {} @ShouldGenerate(r''' SuperUnionRenameScreamingSnake _$SuperUnionRenameScreamingSnakeFromJson( - Map json) => - switch (json['type']) { - 'SUB_UNION_RENAME_SCREAMING_SNAKE' => - _$SubUnionRenameScreamingSnakeFromJson(json), - _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SuperUnionRenameScreamingSnake, - json, - ), - }; + Map json, +) => switch (json['type']) { + 'SUB_UNION_RENAME_SCREAMING_SNAKE' => _$SubUnionRenameScreamingSnakeFromJson( + json, + ), + _ => throw UnrecognizedUnionTypeException( + '${json['type']}', + SuperUnionRenameScreamingSnake, + json, + ), +}; Map _$SuperUnionRenameScreamingSnakeToJson( - SuperUnionRenameScreamingSnake instance) => - switch (instance) { - final SubUnionRenameScreamingSnake instance => { - 'type': 'SUB_UNION_RENAME_SCREAMING_SNAKE', - ..._$SubUnionRenameScreamingSnakeToJson(instance), - }, - }; + SuperUnionRenameScreamingSnake instance, +) => switch (instance) { + final SubUnionRenameScreamingSnake instance => { + 'type': 'SUB_UNION_RENAME_SCREAMING_SNAKE', + ..._$SubUnionRenameScreamingSnakeToJson(instance), + }, +}; ''') @JsonSerializable(unionRename: UnionRename.screamingSnake) sealed class SuperUnionRenameScreamingSnake {} @ShouldGenerate(r''' SubUnionRenameScreamingSnake _$SubUnionRenameScreamingSnakeFromJson( - Map json) => - SubUnionRenameScreamingSnake(); + Map json, +) => SubUnionRenameScreamingSnake(); Map _$SubUnionRenameScreamingSnakeToJson( - SubUnionRenameScreamingSnake instance) => - {}; + SubUnionRenameScreamingSnake instance, +) => {}; ''') @JsonSerializable() class SubUnionRenameScreamingSnake extends SuperUnionRenameScreamingSnake {} From 55a821b559774b2e466fa902d31fb3e763d0bde6 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Fri, 13 Jun 2025 18:21:41 +0300 Subject: [PATCH 15/18] fix: fixes after rebase --- .../lib/complex_sealed_class_examples.g.dart | 43 ++++++++--------- example/lib/sealed_class_example.g.dart | 47 +++++++++---------- json_annotation/CHANGELOG.md | 3 -- json_serializable/CHANGELOG.md | 3 -- .../example/sealed_example.g.dart | 38 +++++++-------- .../lib/src/generator_helper.dart | 23 ++++----- json_serializable/pubspec.yaml | 4 +- 7 files changed, 72 insertions(+), 89 deletions(-) diff --git a/example/lib/complex_sealed_class_examples.g.dart b/example/lib/complex_sealed_class_examples.g.dart index b39e7b90..8a8d0f06 100644 --- a/example/lib/complex_sealed_class_examples.g.dart +++ b/example/lib/complex_sealed_class_examples.g.dart @@ -10,46 +10,43 @@ Organization _$OrganizationFromJson(Map json) => switch (json['organization']) { 'Department' => _$DepartmentFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['organization']}', - Organization, - json, - ), + '${json['organization']}', + Organization, + json, + ), }; Map _$OrganizationToJson(Organization instance) => switch (instance) { final Department instance => { - 'organization': 'Department', - ..._$DepartmentToJson(instance), - }, + 'organization': 'Department', + ..._$DepartmentToJson(instance), + }, }; Department _$DepartmentFromJson(Map json) => switch (json['department']) { 'Team' => _$TeamFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['department']}', - Department, - json, - ), + '${json['department']}', + Department, + json, + ), }; Map _$DepartmentToJson(Department instance) => switch (instance) { - final Team instance => { - 'department': 'Team', - ..._$TeamToJson(instance), - }, + final Team instance => {'department': 'Team', ..._$TeamToJson(instance)}, }; Team _$TeamFromJson(Map json) => Team( - teamLead: json['teamLead'] as String, - departmentHead: json['departmentHead'] as String, - name: json['name'] as String, - ); + teamLead: json['teamLead'] as String, + departmentHead: json['departmentHead'] as String, + name: json['name'] as String, +); Map _$TeamToJson(Team instance) => { - 'name': instance.name, - 'departmentHead': instance.departmentHead, - 'teamLead': instance.teamLead, - }; + 'name': instance.name, + 'departmentHead': instance.departmentHead, + 'teamLead': instance.teamLead, +}; diff --git a/example/lib/sealed_class_example.g.dart b/example/lib/sealed_class_example.g.dart index 860699c8..20b4bfd4 100644 --- a/example/lib/sealed_class_example.g.dart +++ b/example/lib/sealed_class_example.g.dart @@ -11,39 +11,36 @@ Vehicle _$VehicleFromJson(Map json) => 'car' => _$CarFromJson(json), 'bicycle' => _$BicycleFromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['vehicle_type']}', - Vehicle, - json, - ), + '${json['vehicle_type']}', + Vehicle, + json, + ), }; Map _$VehicleToJson(Vehicle instance) => switch (instance) { - final Car instance => { - 'vehicle_type': 'car', - ..._$CarToJson(instance), - }, - final Bicycle instance => { - 'vehicle_type': 'bicycle', - ..._$BicycleToJson(instance), - }, - }; + final Car instance => {'vehicle_type': 'car', ..._$CarToJson(instance)}, + final Bicycle instance => { + 'vehicle_type': 'bicycle', + ..._$BicycleToJson(instance), + }, +}; Car _$CarFromJson(Map json) => Car( - numberOfDoors: (json['numberOfDoors'] as num).toInt(), - vehicleID: json['vehicleID'] as String, - ); + numberOfDoors: (json['numberOfDoors'] as num).toInt(), + vehicleID: json['vehicleID'] as String, +); Map _$CarToJson(Car instance) => { - 'vehicleID': instance.vehicleID, - 'numberOfDoors': instance.numberOfDoors, - }; + 'vehicleID': instance.vehicleID, + 'numberOfDoors': instance.numberOfDoors, +}; Bicycle _$BicycleFromJson(Map json) => Bicycle( - hasBell: json['hasBell'] as bool, - vehicleID: json['vehicleID'] as String, - ); + hasBell: json['hasBell'] as bool, + vehicleID: json['vehicleID'] as String, +); Map _$BicycleToJson(Bicycle instance) => { - 'vehicleID': instance.vehicleID, - 'hasBell': instance.hasBell, - }; + 'vehicleID': instance.vehicleID, + 'hasBell': instance.hasBell, +}; diff --git a/json_annotation/CHANGELOG.md b/json_annotation/CHANGELOG.md index b0ac59e7..84eaea3e 100644 --- a/json_annotation/CHANGELOG.md +++ b/json_annotation/CHANGELOG.md @@ -2,9 +2,6 @@ - Add `JsonSerializable.unionRename` - Add `JsonSerializable.unionDiscriminator` - -## 4.9.1-wip - - Require Dart 3.8 ## 4.9.0 diff --git a/json_serializable/CHANGELOG.md b/json_serializable/CHANGELOG.md index 7876c273..136c769d 100644 --- a/json_serializable/CHANGELOG.md +++ b/json_serializable/CHANGELOG.md @@ -2,9 +2,6 @@ - Add support for deserializing union json to sealed class - Add support for serializing sealed class to union json - -## 6.9.6-wip - - Move `package:collection` to a dev dependency. - Use new `null-aware element` feature in generated code. - Require Dart 3.8 diff --git a/json_serializable/example/sealed_example.g.dart b/json_serializable/example/sealed_example.g.dart index e07877dd..8552b672 100644 --- a/json_serializable/example/sealed_example.g.dart +++ b/json_serializable/example/sealed_example.g.dart @@ -13,38 +13,32 @@ SealedBase _$SealedBaseFromJson(Map json) => 'SealedSub1' => _$SealedSub1FromJson(json), 'SealedSub2' => _$SealedSub2FromJson(json), _ => throw UnrecognizedUnionTypeException( - '${json['type']}', - SealedBase, - json, - ), + '${json['type']}', + SealedBase, + json, + ), }; Map _$SealedBaseToJson(SealedBase instance) => switch (instance) { final SealedSub1 instance => { - 'type': 'SealedSub1', - ..._$SealedSub1ToJson(instance), - }, + 'type': 'SealedSub1', + ..._$SealedSub1ToJson(instance), + }, final SealedSub2 instance => { - 'type': 'SealedSub2', - ..._$SealedSub2ToJson(instance), - }, + 'type': 'SealedSub2', + ..._$SealedSub2ToJson(instance), + }, }; -SealedSub1 _$SealedSub1FromJson(Map json) => SealedSub1( - exampleField1: json['exampleField1'] as String, - ); +SealedSub1 _$SealedSub1FromJson(Map json) => + SealedSub1(exampleField1: json['exampleField1'] as String); Map _$SealedSub1ToJson(SealedSub1 instance) => - { - 'exampleField1': instance.exampleField1, - }; + {'exampleField1': instance.exampleField1}; -SealedSub2 _$SealedSub2FromJson(Map json) => SealedSub2( - exampleField2: json['exampleField2'] as String, - ); +SealedSub2 _$SealedSub2FromJson(Map json) => + SealedSub2(exampleField2: json['exampleField2'] as String); Map _$SealedSub2ToJson(SealedSub2 instance) => - { - 'exampleField2': instance.exampleField2, - }; + {'exampleField2': instance.exampleField2}; diff --git a/json_serializable/lib/src/generator_helper.dart b/json_serializable/lib/src/generator_helper.dart index 63153459..5ecab694 100644 --- a/json_serializable/lib/src/generator_helper.dart +++ b/json_serializable/lib/src/generator_helper.dart @@ -4,7 +4,6 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:build/build.dart'; -import 'package:collection/collection.dart'; import 'package:source_gen/source_gen.dart'; import '../type_helper.dart'; @@ -56,7 +55,7 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ), ); - if (sealedSupersAndConfigs.firstWhereOrNull((e) => e.config == null) + if (sealedSupersAndConfigs.where((e) => e.config == null).firstOrNull case final notAnnotated? when sealedSupersAndConfigs.isNotEmpty) { throw InvalidGenerationSourceError( 'The class `${element.displayName}` is annotated ' @@ -82,9 +81,11 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ); } - if (sealedSupersAndConfigs.firstWhereOrNull( - (e) => e.config?.unionDiscriminator == config.unionDiscriminator, - ) + if (sealedSupersAndConfigs + .where( + (e) => e.config?.unionDiscriminator == config.unionDiscriminator, + ) + .firstOrNull case final conflictingSuper? when element.isSealed) { throw InvalidGenerationSource( 'The classes `${conflictingSuper.classElement.displayName}` and ' @@ -97,9 +98,9 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ); } - if (sealedSupersAndConfigs.firstWhereOrNull( - (e) => e.config?.createToJson != config.createToJson, - ) + if (sealedSupersAndConfigs + .where((e) => e.config?.createToJson != config.createToJson) + .firstOrNull case final diffSuper?) { throw InvalidGenerationSourceError( 'The class `${diffSuper.classElement.displayName}` is sealed but its ' @@ -191,9 +192,9 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { ..fold({}, (Set set, fe) { final jsonKey = nameAccess(fe); - if (sealedSupersAndConfigs.firstWhereOrNull( - (e) => e.config?.unionDiscriminator == jsonKey, - ) + if (sealedSupersAndConfigs + .where((e) => e.config?.unionDiscriminator == jsonKey) + .firstOrNull case final conflict?) { throw InvalidGenerationSourceError( 'The JSON key `$jsonKey` is conflicting with the discriminator ' diff --git a/json_serializable/pubspec.yaml b/json_serializable/pubspec.yaml index 6bb001a0..eb40482e 100644 --- a/json_serializable/pubspec.yaml +++ b/json_serializable/pubspec.yaml @@ -15,11 +15,11 @@ topics: resolution: workspace dependencies: - analyzer: '>=6.9.0 <8.0.0' + analyzer: '>=7.4.0 <8.0.0' async: ^2.10.0 build: ^2.4.1 build_config: ^1.1.0 - dart_style: '>=2.3.7 <4.0.0' + dart_style: '>=3.1.0 <4.0.0' # Use a tight version constraint to ensure that a constraint on # `json_annotation` properly constrains all features it provides. From 199b28086aaad93ed84e757b4de370c0868ab73a Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Fri, 13 Jun 2025 19:52:52 +0300 Subject: [PATCH 16/18] chore: update version constraint syntax --- json_serializable/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json_serializable/pubspec.yaml b/json_serializable/pubspec.yaml index eb40482e..c5312458 100644 --- a/json_serializable/pubspec.yaml +++ b/json_serializable/pubspec.yaml @@ -15,11 +15,11 @@ topics: resolution: workspace dependencies: - analyzer: '>=7.4.0 <8.0.0' + analyzer: ^7.4.0 async: ^2.10.0 build: ^2.4.1 build_config: ^1.1.0 - dart_style: '>=3.1.0 <4.0.0' + dart_style: ^3.1.0 # Use a tight version constraint to ensure that a constraint on # `json_annotation` properly constrains all features it provides. From 92f8499477f6e1618fe4a36a82782eb24e95c9aa Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Fri, 13 Jun 2025 21:47:50 +0300 Subject: [PATCH 17/18] fix: review fixes --- _test_yaml/test/src/build_config.dart | 2 +- .../lib/complex_sealed_class_examples.dart | 13 +--- example/lib/sealed_class_example.dart | 12 +-- .../lib/src/allowed_keys_helpers.dart | 5 +- json_annotation/lib/src/json_enum.dart | 8 +- .../lib/src/json_serializable.dart | 44 ++++------- .../lib/src/json_serializable.g.dart | 22 ++---- json_serializable/example/sealed_example.dart | 8 +- json_serializable/lib/src/decode_helper.dart | 2 +- json_serializable/lib/src/encoder_helper.dart | 2 +- json_serializable/lib/src/enum_utils.dart | 4 +- json_serializable/lib/src/json_key_utils.dart | 2 +- .../lib/src/type_helpers/config_types.dart | 8 +- json_serializable/lib/src/utils.dart | 29 +++---- .../create_per_field_to_json_example.dart | 2 +- .../test/integration/field_map_example.dart | 4 +- .../test/integration/json_enum_example.dart | 2 +- .../test/integration/json_keys_example.dart | 2 +- .../test/integration/json_test_common.dart | 2 +- .../integration/sealed_class_examples.dart | 78 ++++--------------- .../test/json_serializable_test.dart | 2 +- json_serializable/test/shared_config.dart | 4 +- .../src/conflicting_discriminator_input.dart | 4 +- .../test/src/field_namer_input.dart | 10 +-- .../test/src/generic_test_input.dart | 39 ++++------ .../test/src/sealed_test_input.dart | 4 +- .../test/src/union_namer_input.dart | 10 +-- .../test/test_sources/test_sources.dart | 4 +- 28 files changed, 113 insertions(+), 215 deletions(-) diff --git a/_test_yaml/test/src/build_config.dart b/_test_yaml/test/src/build_config.dart index 958329f8..9a7d6e16 100644 --- a/_test_yaml/test/src/build_config.dart +++ b/_test_yaml/test/src/build_config.dart @@ -85,7 +85,7 @@ class Builder { Map toJson() => _$BuilderToJson(this); } -@JsonEnum(fieldRename: FieldRename.snake) +@JsonEnum(fieldRename: RenameType.snake) enum AutoApply { none, dependents, allPackages, rootPackage } enum BuildTo { cache, source } diff --git a/example/lib/complex_sealed_class_examples.dart b/example/lib/complex_sealed_class_examples.dart index df8277a2..e254594f 100644 --- a/example/lib/complex_sealed_class_examples.dart +++ b/example/lib/complex_sealed_class_examples.dart @@ -2,9 +2,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'complex_sealed_class_examples.g.dart'; -@JsonSerializable( - unionDiscriminator: 'organization', -) +@JsonSerializable(unionDiscriminator: 'organization') sealed class Organization { final String name; @@ -16,16 +14,11 @@ sealed class Organization { Map toJson() => _$OrganizationToJson(this); } -@JsonSerializable( - unionDiscriminator: 'department', -) +@JsonSerializable(unionDiscriminator: 'department') sealed class Department extends Organization { final String departmentHead; - Department({ - required this.departmentHead, - required super.name, - }); + Department({required this.departmentHead, required super.name}); factory Department.fromJson(Map json) => _$DepartmentFromJson(json); diff --git a/example/lib/sealed_class_example.dart b/example/lib/sealed_class_example.dart index 8265b511..328baca0 100644 --- a/example/lib/sealed_class_example.dart +++ b/example/lib/sealed_class_example.dart @@ -4,7 +4,7 @@ part 'sealed_class_example.g.dart'; @JsonSerializable( unionDiscriminator: 'vehicle_type', - unionRename: UnionRename.snake, + unionRename: RenameType.snake, ) sealed class Vehicle { final String vehicleID; @@ -21,18 +21,12 @@ sealed class Vehicle { class Car extends Vehicle { final int numberOfDoors; - Car({ - required this.numberOfDoors, - required super.vehicleID, - }); + Car({required this.numberOfDoors, required super.vehicleID}); } @JsonSerializable() class Bicycle extends Vehicle { final bool hasBell; - Bicycle({ - required this.hasBell, - required super.vehicleID, - }); + Bicycle({required this.hasBell, required super.vehicleID}); } diff --git a/json_annotation/lib/src/allowed_keys_helpers.dart b/json_annotation/lib/src/allowed_keys_helpers.dart index b0e87cc8..6729ef33 100644 --- a/json_annotation/lib/src/allowed_keys_helpers.dart +++ b/json_annotation/lib/src/allowed_keys_helpers.dart @@ -89,11 +89,12 @@ class UnrecognizedUnionTypeException extends BadKeyException { final Type unionType; @override - String get message => 'Unrecognized type: $unrecognizedType ' + String get message => + 'Unrecognized type: $unrecognizedType ' 'for union: $unionType.'; UnrecognizedUnionTypeException(this.unrecognizedType, this.unionType, Map map) - : super._(map); + : super._(map); } /// Exception thrown if there are missing required keys in a JSON map that was diff --git a/json_annotation/lib/src/json_enum.dart b/json_annotation/lib/src/json_enum.dart index 27d1fb5b..62140987 100644 --- a/json_annotation/lib/src/json_enum.dart +++ b/json_annotation/lib/src/json_enum.dart @@ -12,7 +12,7 @@ import 'json_value.dart'; class JsonEnum { const JsonEnum({ this.alwaysCreate = false, - this.fieldRename = FieldRename.none, + this.fieldRename = RenameType.none, this.valueField, }); @@ -27,14 +27,14 @@ class JsonEnum { /// Defines the naming strategy when converting enum entry names to JSON /// values. /// - /// With a value [FieldRename.none] (the default), the name of the enum entry + /// With a value [RenameType.none] (the default), the name of the enum entry /// is used without modification. /// - /// See [FieldRename] for details on the other options. + /// See [RenameType] for details on the other options. /// /// Note: the value for [JsonValue.value] takes precedence over this option /// for entries annotated with [JsonValue]. - final FieldRename fieldRename; + final RenameType fieldRename; /// Specifies the field within an "enhanced enum" to use as the value /// to use for serialization. diff --git a/json_annotation/lib/src/json_serializable.dart b/json_annotation/lib/src/json_serializable.dart index 933ce083..9349ba67 100644 --- a/json_annotation/lib/src/json_serializable.dart +++ b/json_annotation/lib/src/json_serializable.dart @@ -12,8 +12,12 @@ import 'json_key.dart'; part 'json_serializable.g.dart'; +// TODO: Remove typedef +@Deprecated('Use RenameType instead') +typedef FieldRename = RenameType; + /// Values for the automatic field renaming behavior for [JsonSerializable]. -enum FieldRename { +enum RenameType { /// Use the field name without changes. none, @@ -31,31 +35,11 @@ enum FieldRename { screamingSnake, } -/// Values for the automatic class renaming behavior for [JsonSerializable] -/// with sealed classes. -enum UnionRename { - /// Use the union class name without changes. - none, - - /// Encodes union class named `KebabCase` with a JSON key `kebab-case`. - kebab, - - /// Encodes union class named `SnakeCase` with a JSON key `snake_case`. - snake, - - /// Encodes union class named `PascalCase` with a JSON key `PascalCase`. - pascal, - - /// Encodes union class named `ScreamingSnakeCase` with a JSON key - /// `SCREAMING_SNAKE_CASE` - screamingSnake, -} - /// An annotation used to specify a class to generate code for. @JsonSerializable( checked: true, disallowUnrecognizedKeys: true, - fieldRename: FieldRename.snake, + fieldRename: RenameType.snake, ) @Target({TargetKind.classType}) class JsonSerializable { @@ -173,14 +157,14 @@ class JsonSerializable { /// Defines the automatic naming strategy when converting class field names /// into JSON map keys. /// - /// With a value [FieldRename.none] (the default), the name of the field is + /// With a value [RenameType.none] (the default), the name of the field is /// used without modification. /// - /// See [FieldRename] for details on the other options. + /// See [RenameType] for details on the other options. /// /// Note: the value for [JsonKey.name] takes precedence over this option for /// fields annotated with [JsonKey]. - final FieldRename? fieldRename; + final RenameType? fieldRename; /// When `true` on classes with type parameters (generic types), extra /// "helper" parameters will be generated for `fromJson` and/or `toJson` to @@ -252,11 +236,11 @@ class JsonSerializable { /// Defines the automatic naming strategy when converting class names /// to union type names. /// - /// With a value [UnionRename.none] (the default), the name of the class is + /// With a value [RenameType.none] (the default), the name of the class is /// used without modification. /// - /// See [UnionRename] for details on the other options. - final UnionRename? unionRename; + /// See [RenameType] for details on the other options. + final RenameType? unionRename; /// A list of [JsonConverter] to apply to this class. /// @@ -328,12 +312,12 @@ class JsonSerializable { createToJson: true, disallowUnrecognizedKeys: false, explicitToJson: false, - fieldRename: FieldRename.none, + fieldRename: RenameType.none, ignoreUnannotated: false, includeIfNull: true, genericArgumentFactories: false, unionDiscriminator: 'type', - unionRename: UnionRename.none, + unionRename: RenameType.none, ); /// Returns a new [JsonSerializable] instance with fields equal to the diff --git a/json_annotation/lib/src/json_serializable.g.dart b/json_annotation/lib/src/json_serializable.g.dart index c6ad60b6..88cd20c7 100644 --- a/json_annotation/lib/src/json_serializable.g.dart +++ b/json_annotation/lib/src/json_serializable.g.dart @@ -69,7 +69,7 @@ JsonSerializable _$JsonSerializableFromJson( ), unionRename: $checkedConvert( 'union_rename', - (v) => $enumDecodeNullable(_$UnionRenameEnumMap, v), + (v) => $enumDecodeNullable(_$FieldRenameEnumMap, v), ), ); return val; @@ -109,21 +109,13 @@ Map _$JsonSerializableToJson(JsonSerializable instance) => 'ignore_unannotated': instance.ignoreUnannotated, 'include_if_null': instance.includeIfNull, 'union_discriminator': instance.unionDiscriminator, - 'union_rename': _$UnionRenameEnumMap[instance.unionRename], + 'union_rename': _$FieldRenameEnumMap[instance.unionRename], }; const _$FieldRenameEnumMap = { - FieldRename.none: 'none', - FieldRename.kebab: 'kebab', - FieldRename.snake: 'snake', - FieldRename.pascal: 'pascal', - FieldRename.screamingSnake: 'screamingSnake', -}; - -const _$UnionRenameEnumMap = { - UnionRename.none: 'none', - UnionRename.kebab: 'kebab', - UnionRename.snake: 'snake', - UnionRename.pascal: 'pascal', - UnionRename.screamingSnake: 'screamingSnake', + RenameType.none: 'none', + RenameType.kebab: 'kebab', + RenameType.snake: 'snake', + RenameType.pascal: 'pascal', + RenameType.screamingSnake: 'screamingSnake', }; diff --git a/json_serializable/example/sealed_example.dart b/json_serializable/example/sealed_example.dart index 17290620..61a5b44c 100644 --- a/json_serializable/example/sealed_example.dart +++ b/json_serializable/example/sealed_example.dart @@ -16,16 +16,12 @@ sealed class SealedBase { class SealedSub1 extends SealedBase { final String exampleField1; - SealedSub1({ - required this.exampleField1, - }); + SealedSub1({required this.exampleField1}); } @JsonSerializable() class SealedSub2 extends SealedBase { final String exampleField2; - SealedSub2({ - required this.exampleField2, - }); + SealedSub2({required this.exampleField2}); } diff --git a/json_serializable/lib/src/decode_helper.dart b/json_serializable/lib/src/decode_helper.dart index 688a09d8..e9145637 100644 --- a/json_serializable/lib/src/decode_helper.dart +++ b/json_serializable/lib/src/decode_helper.dart @@ -146,7 +146,7 @@ mixin DecodeHelper implements HelperCore { final discriminator = config.unionDiscriminator; String buildSingleImpl(ClassElement impl) { - final unionName = encodedUnionName(config.unionRename, impl.name); + final unionName = encodedName(config.unionRename, impl.name); return "'$unionName' => ${classPrefix(impl)}FromJson(json),"; } diff --git a/json_serializable/lib/src/encoder_helper.dart b/json_serializable/lib/src/encoder_helper.dart index 50579a1f..f88ed413 100644 --- a/json_serializable/lib/src/encoder_helper.dart +++ b/json_serializable/lib/src/encoder_helper.dart @@ -145,7 +145,7 @@ mixin EncodeHelper implements HelperCore { String buildSingleImpl(ClassElement impl) { final originalName = impl.name; - final unionName = encodedUnionName(config.unionRename, originalName); + final unionName = encodedName(config.unionRename, originalName); return ''' final $originalName instance => { diff --git a/json_serializable/lib/src/enum_utils.dart b/json_serializable/lib/src/enum_utils.dart index 00f0cbc0..c8c1ae51 100644 --- a/json_serializable/lib/src/enum_utils.dart +++ b/json_serializable/lib/src/enum_utils.dart @@ -124,7 +124,7 @@ Object? _generateEntry({ ); } } else { - return encodedFieldName(jsonEnum.fieldRename, field.name); + return encodedName(jsonEnum.fieldRename, field.name); } } else { final reader = ConstantReader(annotation); @@ -153,7 +153,7 @@ JsonEnum _fromAnnotation(DartObject? dartObject) { final reader = ConstantReader(dartObject); return JsonEnum( alwaysCreate: reader.read('alwaysCreate').literalValue as bool, - fieldRename: readEnum(reader.read('fieldRename'), FieldRename.values)!, + fieldRename: readEnum(reader.read('fieldRename'), RenameType.values)!, valueField: reader.read('valueField').literalValue as String?, ); } diff --git a/json_serializable/lib/src/json_key_utils.dart b/json_serializable/lib/src/json_key_utils.dart index a17e4566..632b1ddc 100644 --- a/json_serializable/lib/src/json_key_utils.dart +++ b/json_serializable/lib/src/json_key_utils.dart @@ -307,7 +307,7 @@ KeyConfig _populateJsonKey( disallowNullValue, classAnnotation.includeIfNull, ), - name: name ?? encodedFieldName(classAnnotation.fieldRename, element.name), + name: name ?? encodedName(classAnnotation.fieldRename, element.name), readValueFunctionName: readValueFunctionName, required: required ?? false, unknownEnumValue: unknownEnumValue, diff --git a/json_serializable/lib/src/type_helpers/config_types.dart b/json_serializable/lib/src/type_helpers/config_types.dart index 9d6002d5..1b57a70c 100644 --- a/json_serializable/lib/src/type_helpers/config_types.dart +++ b/json_serializable/lib/src/type_helpers/config_types.dart @@ -53,14 +53,14 @@ class ClassConfig { final bool createPerFieldToJson; final bool disallowUnrecognizedKeys; final bool explicitToJson; - final FieldRename fieldRename; + final RenameType fieldRename; final bool genericArgumentFactories; final bool ignoreUnannotated; final bool includeIfNull; final Map ctorParamDefaults; final List converters; final String unionDiscriminator; - final UnionRename unionRename; + final RenameType unionRename; const ClassConfig({ required this.anyMap, @@ -132,12 +132,12 @@ class ClassConfig { createPerFieldToJson: false, disallowUnrecognizedKeys: false, explicitToJson: false, - fieldRename: FieldRename.none, + fieldRename: RenameType.none, genericArgumentFactories: false, ignoreUnannotated: false, includeIfNull: true, unionDiscriminator: 'type', - unionRename: UnionRename.none, + unionRename: RenameType.none, ); JsonSerializable toJsonSerializable() => JsonSerializable( diff --git a/json_serializable/lib/src/utils.dart b/json_serializable/lib/src/utils.dart index c82f1fa7..eb5ae8fb 100644 --- a/json_serializable/lib/src/utils.dart +++ b/json_serializable/lib/src/utils.dart @@ -62,13 +62,13 @@ JsonSerializable _valueForAnnotation(ConstantReader reader) => JsonSerializable( disallowUnrecognizedKeys: reader.read('disallowUnrecognizedKeys').literalValue as bool?, explicitToJson: reader.read('explicitToJson').literalValue as bool?, - fieldRename: readEnum(reader.read('fieldRename'), FieldRename.values), + fieldRename: readEnum(reader.read('fieldRename'), RenameType.values), genericArgumentFactories: reader.read('genericArgumentFactories').literalValue as bool?, ignoreUnannotated: reader.read('ignoreUnannotated').literalValue as bool?, includeIfNull: reader.read('includeIfNull').literalValue as bool?, unionDiscriminator: reader.read('unionDiscriminator').literalValue as String?, - unionRename: readEnum(reader.read('unionRename'), UnionRename.values), + unionRename: readEnum(reader.read('unionRename'), RenameType.values), ); /// Returns a [ClassConfig] with values from the [JsonSerializable] @@ -174,9 +174,7 @@ ConstructorElement constructorByName(ClassElement classElement, String name) { /// indirect subclasses (ie. subclasses of subclasses). /// /// Otherwise, returns an empty iterable. -Iterable sealedSubClasses( - ClassElement maybeSealedSuperClass, -) { +Iterable sealedSubClasses(ClassElement maybeSealedSuperClass) { if (maybeSealedSuperClass case final sc when sc.isSealed) { return LibraryReader( sc.library, @@ -251,22 +249,13 @@ extension DartTypeExtension on DartType { String ifNullOrElse(String test, String ifNull, String ifNotNull) => '$test == null ? $ifNull : $ifNotNull'; -String encodedFieldName(FieldRename fieldRename, String declaredName) => +String encodedName(RenameType fieldRename, String declaredName) => switch (fieldRename) { - FieldRename.none => declaredName, - FieldRename.snake => declaredName.snake, - FieldRename.screamingSnake => declaredName.snake.toUpperCase(), - FieldRename.kebab => declaredName.kebab, - FieldRename.pascal => declaredName.pascal, - }; - -String encodedUnionName(UnionRename unionRename, String declaredName) => - switch (unionRename) { - UnionRename.none => declaredName, - UnionRename.snake => declaredName.snake, - UnionRename.screamingSnake => declaredName.snake.toUpperCase(), - UnionRename.kebab => declaredName.kebab, - UnionRename.pascal => declaredName.pascal, + RenameType.none => declaredName, + RenameType.snake => declaredName.snake, + RenameType.screamingSnake => declaredName.snake.toUpperCase(), + RenameType.kebab => declaredName.kebab, + RenameType.pascal => declaredName.pascal, }; String classPrefix(ClassElement element) => '_\$${element.name.nonPrivate}'; diff --git a/json_serializable/test/integration/create_per_field_to_json_example.dart b/json_serializable/test/integration/create_per_field_to_json_example.dart index d086253f..93a11bed 100644 --- a/json_serializable/test/integration/create_per_field_to_json_example.dart +++ b/json_serializable/test/integration/create_per_field_to_json_example.dart @@ -73,7 +73,7 @@ typedef GenericFactoryPerFieldToJson = _$GenericFactoryPerFieldToJson; @JsonSerializable( createPerFieldToJson: true, - fieldRename: FieldRename.kebab, + fieldRename: RenameType.kebab, createFactory: false, ) class _PrivateModel { diff --git a/json_serializable/test/integration/field_map_example.dart b/json_serializable/test/integration/field_map_example.dart index 3eef4469..acc81ee4 100644 --- a/json_serializable/test/integration/field_map_example.dart +++ b/json_serializable/test/integration/field_map_example.dart @@ -2,7 +2,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'field_map_example.g.dart'; -@JsonSerializable(createFieldMap: true, fieldRename: FieldRename.kebab) +@JsonSerializable(createFieldMap: true, fieldRename: RenameType.kebab) class Model { Model({required this.firstName, required this.lastName, this.ignoredName}); @@ -25,7 +25,7 @@ const modelFieldMap = _$ModelFieldMap; @JsonSerializable( createFieldMap: true, - fieldRename: FieldRename.kebab, + fieldRename: RenameType.kebab, createFactory: false, ) class _PrivateModel { diff --git a/json_serializable/test/integration/json_enum_example.dart b/json_serializable/test/integration/json_enum_example.dart index f36159cc..85cba625 100644 --- a/json_serializable/test/integration/json_enum_example.dart +++ b/json_serializable/test/integration/json_enum_example.dart @@ -16,7 +16,7 @@ enum StandAloneEnum { Iterable get standAloneEnumValues => _$StandAloneEnumEnumMap.values; -@JsonEnum(alwaysCreate: true, fieldRename: FieldRename.kebab) +@JsonEnum(alwaysCreate: true, fieldRename: RenameType.kebab) enum DayType { noGood, rotten, veryBad } Iterable get dayTypeEnumValues => _$DayTypeEnumMap.values; diff --git a/json_serializable/test/integration/json_keys_example.dart b/json_serializable/test/integration/json_keys_example.dart index 985979d2..a5913628 100644 --- a/json_serializable/test/integration/json_keys_example.dart +++ b/json_serializable/test/integration/json_keys_example.dart @@ -2,7 +2,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'json_keys_example.g.dart'; -@JsonSerializable(createJsonKeys: true, fieldRename: FieldRename.kebab) +@JsonSerializable(createJsonKeys: true, fieldRename: RenameType.kebab) class Model { Model({required this.firstName, required this.lastName, this.ignoredName}); diff --git a/json_serializable/test/integration/json_test_common.dart b/json_serializable/test/integration/json_test_common.dart index 95372161..b0f821c1 100644 --- a/json_serializable/test/integration/json_test_common.dart +++ b/json_serializable/test/integration/json_test_common.dart @@ -6,7 +6,7 @@ import 'dart:collection'; import 'package:json_annotation/json_annotation.dart'; -@JsonEnum(fieldRename: FieldRename.kebab) +@JsonEnum(fieldRename: RenameType.kebab) enum Category { top, bottom, diff --git a/json_serializable/test/integration/sealed_class_examples.dart b/json_serializable/test/integration/sealed_class_examples.dart index 23a9fb35..bb54279d 100644 --- a/json_serializable/test/integration/sealed_class_examples.dart +++ b/json_serializable/test/integration/sealed_class_examples.dart @@ -22,10 +22,7 @@ sealed class Vehicle { class Car extends Vehicle { final int numberOfDoors; - Car({ - required this.numberOfDoors, - required super.vehicleID, - }); + Car({required this.numberOfDoors, required super.vehicleID}); @override bool operator ==(Object other) => @@ -43,10 +40,7 @@ class Car extends Vehicle { class Bicycle extends Vehicle { final bool hasBell; - Bicycle({ - required this.hasBell, - required super.vehicleID, - }); + Bicycle({required this.hasBell, required super.vehicleID}); @override bool operator ==(Object other) => @@ -62,14 +56,12 @@ class Bicycle extends Vehicle { @JsonSerializable( unionDiscriminator: 'delivery_type', - unionRename: UnionRename.snake, + unionRename: RenameType.snake, ) sealed class Delivery { final int deliveryID; - Delivery({ - required this.deliveryID, - }); + Delivery({required this.deliveryID}); factory Delivery.fromJson(Map json) => _$DeliveryFromJson(json); @@ -81,10 +73,7 @@ sealed class Delivery { class DroneDelivery extends Delivery { final int droneModel; - DroneDelivery({ - required this.droneModel, - required super.deliveryID, - }); + DroneDelivery({required this.droneModel, required super.deliveryID}); @override bool operator ==(Object other) => @@ -102,10 +91,7 @@ class DroneDelivery extends Delivery { class TruckDelivery extends Delivery { final double weightCapacity; - TruckDelivery({ - required this.weightCapacity, - required super.deliveryID, - }); + TruckDelivery({required this.weightCapacity, required super.deliveryID}); @override bool operator ==(Object other) => @@ -119,9 +105,7 @@ class TruckDelivery extends Delivery { int get hashCode => jsonEncode(this).hashCode; } -@JsonSerializable( - unionDiscriminator: 'organization', -) +@JsonSerializable(unionDiscriminator: 'organization') sealed class Organization { final String name; @@ -133,16 +117,11 @@ sealed class Organization { Map toJson() => _$OrganizationToJson(this); } -@JsonSerializable( - unionDiscriminator: 'department', -) +@JsonSerializable(unionDiscriminator: 'department') sealed class Department extends Organization { final String departmentHead; - Department({ - required this.departmentHead, - required super.name, - }); + Department({required this.departmentHead, required super.name}); factory Department.fromJson(Map json) => _$DepartmentFromJson(json); @@ -195,9 +174,7 @@ sealed class Trackable { class Ship implements Transportable { final double cargoCapacity; - Ship({ - required this.cargoCapacity, - }); + Ship({required this.cargoCapacity}); @override bool operator ==(Object other) => @@ -217,9 +194,7 @@ class Ship implements Transportable { class GPSDevice implements Trackable { final String serialNumber; - GPSDevice({ - required this.serialNumber, - }); + GPSDevice({required this.serialNumber}); @override bool operator ==(Object other) => @@ -240,10 +215,7 @@ class Package implements Transportable, Trackable { final String label; final double weight; - Package({ - required this.label, - required this.weight, - }); + Package({required this.label, required this.weight}); @override bool operator ==(Object other) => @@ -265,10 +237,7 @@ sealed class Container { final String containerID; final List items; - Container({ - required this.containerID, - this.items = const [], - }); + Container({required this.containerID, this.items = const []}); factory Container.fromJson(Map json) => _$ContainerFromJson(json); @@ -281,10 +250,7 @@ sealed class StorageItem { final String itemID; final Container? nestedContainer; - StorageItem({ - required this.itemID, - this.nestedContainer, - }); + StorageItem({required this.itemID, this.nestedContainer}); factory StorageItem.fromJson(Map json) => _$StorageItemFromJson(json); @@ -294,10 +260,7 @@ sealed class StorageItem { @JsonSerializable() class Box extends Container { - Box({ - required super.containerID, - super.items, - }); + Box({required super.containerID, super.items}); @override bool operator ==(Object other) => @@ -313,10 +276,7 @@ class Box extends Container { @JsonSerializable() class Parcel extends Container { - Parcel({ - required super.containerID, - super.items, - }); + Parcel({required super.containerID, super.items}); @override bool operator ==(Object other) => @@ -334,11 +294,7 @@ class Parcel extends Container { class Product extends StorageItem { final String name; - Product({ - required this.name, - required super.itemID, - super.nestedContainer, - }); + Product({required this.name, required super.itemID, super.nestedContainer}); @override bool operator ==(Object other) => diff --git a/json_serializable/test/json_serializable_test.dart b/json_serializable/test/json_serializable_test.dart index ef562246..841b9d78 100644 --- a/json_serializable/test/json_serializable_test.dart +++ b/json_serializable/test/json_serializable_test.dart @@ -209,5 +209,5 @@ const _expectedAnnotatedTests = { 'WrongConstructorNameClass', '_BetterPrivateNames', 'annotatedMethod', - 'theAnswer' + 'theAnswer', }; diff --git a/json_serializable/test/shared_config.dart b/json_serializable/test/shared_config.dart index 8bf46616..6ffec955 100644 --- a/json_serializable/test/shared_config.dart +++ b/json_serializable/test/shared_config.dart @@ -24,11 +24,11 @@ final generatorConfigNonDefaultJson = Map.unmodifiable( createPerFieldToJson: true, disallowUnrecognizedKeys: true, explicitToJson: true, - fieldRename: FieldRename.kebab, + fieldRename: RenameType.kebab, ignoreUnannotated: true, includeIfNull: false, genericArgumentFactories: true, unionDiscriminator: 'runtimeType', - unionRename: UnionRename.kebab, + unionRename: RenameType.kebab, ).toJson(), ); diff --git a/json_serializable/test/src/conflicting_discriminator_input.dart b/json_serializable/test/src/conflicting_discriminator_input.dart index fa2fc235..4bbef903 100644 --- a/json_serializable/test/src/conflicting_discriminator_input.dart +++ b/json_serializable/test/src/conflicting_discriminator_input.dart @@ -11,7 +11,7 @@ sealed class SuperWithConflictingSnakeCaseDiscriminator {} todo: 'Rename the field or the discriminator.', element: 'thisWillConflict', ) -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable(fieldRename: RenameType.snake) class SubWithConflictingDiscriminatorWithFieldRenameExt extends SuperWithConflictingSnakeCaseDiscriminator { final String thisWillConflict; @@ -27,7 +27,7 @@ class SubWithConflictingDiscriminatorWithFieldRenameExt todo: 'Rename the field or the discriminator.', element: 'thisWillConflict', ) -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable(fieldRename: RenameType.snake) class SubWithConflictingDiscriminatorWithFieldRenameImpl implements SuperWithConflictingSnakeCaseDiscriminator { final String thisWillConflict; diff --git a/json_serializable/test/src/field_namer_input.dart b/json_serializable/test/src/field_namer_input.dart index 6541857e..43188fe3 100644 --- a/json_serializable/test/src/field_namer_input.dart +++ b/json_serializable/test/src/field_namer_input.dart @@ -9,7 +9,7 @@ Map _$FieldNamerNoneToJson(FieldNamerNone instance) => 'NAME_OVERRIDE': instance.nameOverride, }; ''') -@JsonSerializable(fieldRename: FieldRename.none, createFactory: false) +@JsonSerializable(fieldRename: RenameType.none, createFactory: false) class FieldNamerNone { late String theField; @@ -24,7 +24,7 @@ Map _$FieldNamerKebabToJson(FieldNamerKebab instance) => 'NAME_OVERRIDE': instance.nameOverride, }; ''') -@JsonSerializable(fieldRename: FieldRename.kebab, createFactory: false) +@JsonSerializable(fieldRename: RenameType.kebab, createFactory: false) class FieldNamerKebab { late String theField; @@ -39,7 +39,7 @@ Map _$FieldNamerPascalToJson(FieldNamerPascal instance) => 'NAME_OVERRIDE': instance.nameOverride, }; ''') -@JsonSerializable(fieldRename: FieldRename.pascal, createFactory: false) +@JsonSerializable(fieldRename: RenameType.pascal, createFactory: false) class FieldNamerPascal { late String theField; @@ -54,7 +54,7 @@ Map _$FieldNamerSnakeToJson(FieldNamerSnake instance) => 'NAME_OVERRIDE': instance.nameOverride, }; ''') -@JsonSerializable(fieldRename: FieldRename.snake, createFactory: false) +@JsonSerializable(fieldRename: RenameType.snake, createFactory: false) class FieldNamerSnake { late String theField; @@ -70,7 +70,7 @@ Map _$FieldNamerScreamingSnakeToJson( 'nameOverride': instance.nameOverride, }; ''') -@JsonSerializable(fieldRename: FieldRename.screamingSnake, createFactory: false) +@JsonSerializable(fieldRename: RenameType.screamingSnake, createFactory: false) class FieldNamerScreamingSnake { late String theField; diff --git a/json_serializable/test/src/generic_test_input.dart b/json_serializable/test/src/generic_test_input.dart index aa8be42b..6d50ad44 100644 --- a/json_serializable/test/src/generic_test_input.dart +++ b/json_serializable/test/src/generic_test_input.dart @@ -91,18 +91,15 @@ class GenericArgumentFactoriesFlagWithoutGenericType {} 'with `JsonSerializable` field `genericArgumentFactories: true`. ' '`genericArgumentFactories: true` is not supported for classes ' 'that are sealed or have sealed superclasses.', - todo: 'Remove the `genericArgumentFactories` option or ' + todo: + 'Remove the `genericArgumentFactories` option or ' 'remove the `sealed` keyword from the class.', element: 'SuperWithGenericArgumentFactories', ) -@JsonSerializable( - genericArgumentFactories: true, -) +@JsonSerializable(genericArgumentFactories: true) sealed class SuperWithGenericArgumentFactories {} -@JsonSerializable( - genericArgumentFactories: false, -) +@JsonSerializable(genericArgumentFactories: false) sealed class SuperWithoutGenericArgumentFactories {} @ShouldThrow( @@ -110,13 +107,12 @@ sealed class SuperWithoutGenericArgumentFactories {} 'with `JsonSerializable` field `genericArgumentFactories: true`. ' '`genericArgumentFactories: true` is not supported for classes ' 'that are sealed or have sealed superclasses.', - todo: 'Remove the `genericArgumentFactories` option or ' + todo: + 'Remove the `genericArgumentFactories` option or ' 'remove the `sealed` keyword from the class.', element: 'SubWithSubGenericArgumentFactoriesExt', ) -@JsonSerializable( - genericArgumentFactories: true, -) +@JsonSerializable(genericArgumentFactories: true) class SubWithSubGenericArgumentFactoriesExt extends SuperWithoutGenericArgumentFactories {} @@ -125,13 +121,12 @@ class SubWithSubGenericArgumentFactoriesExt 'with `JsonSerializable` field `genericArgumentFactories: true`. ' '`genericArgumentFactories: true` is not supported for classes ' 'that are sealed or have sealed superclasses.', - todo: 'Remove the `genericArgumentFactories` option or ' + todo: + 'Remove the `genericArgumentFactories` option or ' 'remove the `sealed` keyword from the class.', element: 'SubWithSubGenericArgumentFactoriesImpl', ) -@JsonSerializable( - genericArgumentFactories: true, -) +@JsonSerializable(genericArgumentFactories: true) class SubWithSubGenericArgumentFactoriesImpl implements SuperWithoutGenericArgumentFactories {} @@ -140,13 +135,12 @@ class SubWithSubGenericArgumentFactoriesImpl 'with `JsonSerializable` field `genericArgumentFactories: true`. ' '`genericArgumentFactories: true` is not supported for classes ' 'that are sealed or have sealed superclasses.', - todo: 'Remove the `genericArgumentFactories` option or ' + todo: + 'Remove the `genericArgumentFactories` option or ' 'remove the `sealed` keyword from the class.', element: 'SubWithSubAndSuperGenericArgumentFactoriesExt', ) -@JsonSerializable( - genericArgumentFactories: true, -) +@JsonSerializable(genericArgumentFactories: true) class SubWithSubAndSuperGenericArgumentFactoriesExt extends SuperWithGenericArgumentFactories {} @@ -155,12 +149,11 @@ class SubWithSubAndSuperGenericArgumentFactoriesExt 'with `JsonSerializable` field `genericArgumentFactories: true`. ' '`genericArgumentFactories: true` is not supported for classes ' 'that are sealed or have sealed superclasses.', - todo: 'Remove the `genericArgumentFactories` option or ' + todo: + 'Remove the `genericArgumentFactories` option or ' 'remove the `sealed` keyword from the class.', element: 'SubWithSubAndSuperGenericArgumentFactoriesImpl', ) -@JsonSerializable( - genericArgumentFactories: true, -) +@JsonSerializable(genericArgumentFactories: true) class SubWithSubAndSuperGenericArgumentFactoriesImpl implements SuperWithGenericArgumentFactories {} diff --git a/json_serializable/test/src/sealed_test_input.dart b/json_serializable/test/src/sealed_test_input.dart index 6711b0eb..882e7e78 100644 --- a/json_serializable/test/src/sealed_test_input.dart +++ b/json_serializable/test/src/sealed_test_input.dart @@ -168,7 +168,7 @@ Map _$SuperSimpleSealedClassWithChangedUnionRenameToJson( }, }; ''') -@JsonSerializable(unionRename: UnionRename.snake) +@JsonSerializable(unionRename: RenameType.snake) sealed class SuperSimpleSealedClassWithChangedUnionRename { const SuperSimpleSealedClassWithChangedUnionRename(); } @@ -259,7 +259,7 @@ _$SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRenameToJson( ''') @JsonSerializable( unionDiscriminator: 'my_discriminator', - unionRename: UnionRename.kebab, + unionRename: RenameType.kebab, ) sealed class SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename { const SuperSimpleSealedClassWithChangedDiscriminatorAndUnionRename(); diff --git a/json_serializable/test/src/union_namer_input.dart b/json_serializable/test/src/union_namer_input.dart index 80b7734c..80081c00 100644 --- a/json_serializable/test/src/union_namer_input.dart +++ b/json_serializable/test/src/union_namer_input.dart @@ -23,7 +23,7 @@ Map _$SuperUnionRenameNoneToJson( }, }; ''') -@JsonSerializable(unionRename: UnionRename.none) +@JsonSerializable(unionRename: RenameType.none) sealed class SuperUnionRenameNone {} @ShouldGenerate(r''' @@ -57,7 +57,7 @@ Map _$SuperUnionRenameKebabToJson( }, }; ''') -@JsonSerializable(unionRename: UnionRename.kebab) +@JsonSerializable(unionRename: RenameType.kebab) sealed class SuperUnionRenameKebab {} @ShouldGenerate(r''' @@ -92,7 +92,7 @@ Map _$SuperUnionRenameSnakeToJson( }, }; ''') -@JsonSerializable(unionRename: UnionRename.snake) +@JsonSerializable(unionRename: RenameType.snake) sealed class SuperUnionRenameSnake {} @ShouldGenerate(r''' @@ -127,7 +127,7 @@ Map _$SuperUnionRenamePascalToJson( }, }; ''') -@JsonSerializable(unionRename: UnionRename.pascal) +@JsonSerializable(unionRename: RenameType.pascal) sealed class SuperUnionRenamePascal {} @ShouldGenerate(r''' @@ -165,7 +165,7 @@ Map _$SuperUnionRenameScreamingSnakeToJson( }, }; ''') -@JsonSerializable(unionRename: UnionRename.screamingSnake) +@JsonSerializable(unionRename: RenameType.screamingSnake) sealed class SuperUnionRenameScreamingSnake {} @ShouldGenerate(r''' diff --git a/json_serializable/test/test_sources/test_sources.dart b/json_serializable/test/test_sources/test_sources.dart index 959edeed..e25fc810 100644 --- a/json_serializable/test/test_sources/test_sources.dart +++ b/json_serializable/test/test_sources/test_sources.dart @@ -20,12 +20,12 @@ class ConfigurationImplicitDefaults { createPerFieldToJson: false, disallowUnrecognizedKeys: false, explicitToJson: false, - fieldRename: FieldRename.none, + fieldRename: RenameType.none, ignoreUnannotated: false, includeIfNull: true, genericArgumentFactories: false, unionDiscriminator: 'type', - unionRename: UnionRename.none, + unionRename: RenameType.none, ) class ConfigurationExplicitDefaults { int? field; From 21972edf51a5e863c9b12327845f8e3b86fcdd72 Mon Sep 17 00:00:00 2001 From: Onnimanni Hannonen Date: Fri, 13 Jun 2025 22:04:36 +0300 Subject: [PATCH 18/18] fix: review fixes --- json_annotation/CHANGELOG.md | 1 + json_annotation/lib/src/json_serializable.g.dart | 10 +++++----- json_serializable/README.md | 2 +- json_serializable/tool/readme/readme_template.md | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/json_annotation/CHANGELOG.md b/json_annotation/CHANGELOG.md index 84eaea3e..0473d8af 100644 --- a/json_annotation/CHANGELOG.md +++ b/json_annotation/CHANGELOG.md @@ -3,6 +3,7 @@ - Add `JsonSerializable.unionRename` - Add `JsonSerializable.unionDiscriminator` - Require Dart 3.8 +- Deprecated `FieldRename` in favor of `RenameType` ## 4.9.0 diff --git a/json_annotation/lib/src/json_serializable.g.dart b/json_annotation/lib/src/json_serializable.g.dart index 88cd20c7..794812ce 100644 --- a/json_annotation/lib/src/json_serializable.g.dart +++ b/json_annotation/lib/src/json_serializable.g.dart @@ -48,7 +48,7 @@ JsonSerializable _$JsonSerializableFromJson( explicitToJson: $checkedConvert('explicit_to_json', (v) => v as bool?), fieldRename: $checkedConvert( 'field_rename', - (v) => $enumDecodeNullable(_$FieldRenameEnumMap, v), + (v) => $enumDecodeNullable(_$RenameTypeEnumMap, v), ), ignoreUnannotated: $checkedConvert( 'ignore_unannotated', @@ -69,7 +69,7 @@ JsonSerializable _$JsonSerializableFromJson( ), unionRename: $checkedConvert( 'union_rename', - (v) => $enumDecodeNullable(_$FieldRenameEnumMap, v), + (v) => $enumDecodeNullable(_$RenameTypeEnumMap, v), ), ); return val; @@ -104,15 +104,15 @@ Map _$JsonSerializableToJson(JsonSerializable instance) => 'create_to_json': instance.createToJson, 'disallow_unrecognized_keys': instance.disallowUnrecognizedKeys, 'explicit_to_json': instance.explicitToJson, - 'field_rename': _$FieldRenameEnumMap[instance.fieldRename], + 'field_rename': _$RenameTypeEnumMap[instance.fieldRename], 'generic_argument_factories': instance.genericArgumentFactories, 'ignore_unannotated': instance.ignoreUnannotated, 'include_if_null': instance.includeIfNull, 'union_discriminator': instance.unionDiscriminator, - 'union_rename': _$FieldRenameEnumMap[instance.unionRename], + 'union_rename': _$RenameTypeEnumMap[instance.unionRename], }; -const _$FieldRenameEnumMap = { +const _$RenameTypeEnumMap = { RenameType.none: 'none', RenameType.kebab: 'kebab', RenameType.snake: 'snake', diff --git a/json_serializable/README.md b/json_serializable/README.md index 7dcee747..b5c583f0 100644 --- a/json_serializable/README.md +++ b/json_serializable/README.md @@ -104,7 +104,7 @@ precedence over any value set on [`JsonSerializable`]. Annotate `enum` types with [`JsonEnum`] (new in `json_annotation` 4.2.0) to: 1. Specify the default rename logic for each enum value using `fieldRename`. For - instance, use `fieldRename: FieldRename.kebab` to encode `enum` value + instance, use `fieldRename: RenameType.kebab` to encode `enum` value `noGood` as `"no-good"`. 1. Force the generation of the `enum` helpers, even if the `enum` is not referenced in code. This is an edge scenario, but useful for some. diff --git a/json_serializable/tool/readme/readme_template.md b/json_serializable/tool/readme/readme_template.md index 19c4af8e..c377a4ac 100644 --- a/json_serializable/tool/readme/readme_template.md +++ b/json_serializable/tool/readme/readme_template.md @@ -64,7 +64,7 @@ precedence over any value set on `ja:JsonSerializable`. Annotate `enum` types with `ja:JsonEnum` (new in `json_annotation` 4.2.0) to: 1. Specify the default rename logic for each enum value using `fieldRename`. For - instance, use `fieldRename: FieldRename.kebab` to encode `enum` value + instance, use `fieldRename: RenameType.kebab` to encode `enum` value `noGood` as `"no-good"`. 1. Force the generation of the `enum` helpers, even if the `enum` is not referenced in code. This is an edge scenario, but useful for some.