diff --git a/pkg/json/CHANGELOG.md b/pkg/json/CHANGELOG.md index b78404b21bc5..f7a36b99905e 100644 --- a/pkg/json/CHANGELOG.md +++ b/pkg/json/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.20.4 + +- Allow custom `fromJson` to have an arbitrary parameter type. + # 0.20.3 - Add support for DateTime, serializing it to an ISO-8601 String. diff --git a/pkg/json/lib/json.dart b/pkg/json/lib/json.dart index 1a2f4ddfdabf..653acfee8c15 100644 --- a/pkg/json/lib/json.dart +++ b/pkg/json/lib/json.dart @@ -394,16 +394,16 @@ mixin _FromJson on _Shared { // Otherwise, check if `classDecl` has a `fromJson` constructor. final constructors = await builder.constructorsOf(classDecl); final fromJson = constructors - .firstWhereOrNull((c) => c.identifier.name == 'fromJson') - ?.identifier; + .firstWhereOrNull((c) => c.identifier.name == 'fromJson'); + if (fromJson != null) { return RawCode.fromParts([ if (nullCheck != null) nullCheck, - fromJson, + fromJson.identifier, '(', jsonReference, ' as ', - introspectionData.jsonMapCode, + fromJson.positionalParameters.first.type.code, ')', ]); } diff --git a/pkg/json/pubspec.yaml b/pkg/json/pubspec.yaml index e2eb306c0d6b..5e06793d7534 100644 --- a/pkg/json/pubspec.yaml +++ b/pkg/json/pubspec.yaml @@ -5,7 +5,7 @@ description: > `toJson` encoding method. repository: https://github.com/dart-lang/sdk/tree/main/pkg/json -version: 0.20.3 +version: 0.20.4 environment: sdk: ^3.6.0-edge dependencies: diff --git a/pkg/json/test/json_codable_test.dart b/pkg/json/test/json_codable_test.dart index 15f48bc1ec4a..4c0daf9e3448 100644 --- a/pkg/json/test/json_codable_test.dart +++ b/pkg/json/test/json_codable_test.dart @@ -26,6 +26,11 @@ void main() { 'mapOfSerializableField': { 'c': {'x': 3} }, + 'customStringSerializationField': 'hi', + 'customNullableStringSerialization': null, + 'customMapSerializationField': { + 'z': 'zzz', + } }; var a = A.fromJson(json); @@ -38,6 +43,11 @@ void main() { expect(a.listOfSerializableField.single.x, 1); expect(a.setOfSerializableField.single.x, 2); expect(a.mapOfSerializableField['c']!.x, 3); + expect(a.customStringSerializationField, CustomStringSerialization('hi')); + expect(a.customNullableStringSerialization, + equals(CustomNullableStringSerialization(null))); + expect( + a.customMapSerializationField, CustomMapSerialization({'z': 'zzz'})); expect(a.toJson(), equals(json)); }); @@ -59,6 +69,8 @@ void main() { 'nullableMapOfSerializableField': { 'd': {'x': 3}, }, + 'nullableCustomStringSerializationField': 'hi', + 'nullableCustomMapSerializationField': {'z': 'zzz'}, }; var b = B.fromJson(json); @@ -72,6 +84,10 @@ void main() { expect(b.nullableListOfSerializableField!.single.x, 1); expect(b.nullableSetOfSerializableField!.single.x, 2); expect(b.nullableMapOfSerializableField!['d']!.x, 3); + expect(b.nullableCustomStringSerializationField, + CustomStringSerialization('hi')); + expect(b.nullableCustomMapSerializationField, + CustomMapSerialization({'z': 'zzz'})); expect(b.toJson(), equals(json)); }); @@ -87,6 +103,8 @@ void main() { 'nullableListOfSerializableField': null, 'nullableSetOfSerializableField': null, 'nullableMapOfSerializableField': null, + 'nullableCustomStringSerializationField': null, + 'nullableCustomMapSerializationField': null, }); expect(b.nullableBoolField, null); expect(b.nullableStringField, null); @@ -97,6 +115,8 @@ void main() { expect(b.nullableListOfSerializableField, null); expect(b.nullableMapOfSerializableField, null); expect(b.nullableSetOfSerializableField, null); + expect(b.nullableCustomStringSerializationField, null); + expect(b.nullableCustomMapSerializationField, null); expect(b.toJson(), isEmpty); }); @@ -112,6 +132,8 @@ void main() { expect(b.nullableListOfSerializableField, null); expect(b.nullableMapOfSerializableField, null); expect(b.nullableSetOfSerializableField, null); + expect(b.nullableCustomStringSerializationField, null); + expect(b.nullableCustomMapSerializationField, null); expect(b.toJson(), isEmpty); }); @@ -140,6 +162,11 @@ void main() { null, {'a': 1, 'b': null}, ], + 'listOfCustomStringSerializables': [null, 'hi'], + 'listOfCustomMapSerializables': [ + null, + {'z': 'zzz'}, + ], 'setOfNullableDates': [ null, '2024-11-12T03:42:29.108308', @@ -159,6 +186,11 @@ void main() { 'b': null, }, ], + 'setOfCustomStringSerializables': [null, 'hi'], + 'setOfCustomMapSerializables': [ + null, + {'z': 'zzz'} + ], 'mapOfNullableDates': { 'a': '2024-11-13T03:42:29.108308', 'b': null, @@ -175,6 +207,14 @@ void main() { 'a': [null, 3], 'b': null, }, + 'mapOfCustomStringSerializables': { + 'a': null, + 'b': 'hi', + }, + 'mapOfCustomMapSerializables': { + 'a': null, + 'b': {'z': 'zzz'}, + }, }; var e = E.fromJson(json); @@ -189,6 +229,13 @@ void main() { null, {'a': 1, 'b': null}, ])); + expect(e.listOfCustomStringSerializables.first, null); + expect(e.listOfCustomStringSerializables[1], + CustomStringSerialization('hi')); + expect(e.listOfCustomMapSerializables.first, null); + expect(e.listOfCustomMapSerializables[1], + CustomMapSerialization({'z': 'zzz'})); + expect(e.setOfNullableDates, equals([null, DateTime.parse('2024-11-12T03:42:29.108308')])); expect(e.setOfNullableInts, equals({null, 2})); @@ -203,6 +250,13 @@ void main() { 'b': null, }, })); + expect(e.setOfCustomStringSerializables.first, null); + expect(e.setOfCustomStringSerializables.toList()[1], + CustomStringSerialization('hi')); + expect(e.setOfCustomMapSerializables.first, null); + expect(e.setOfCustomMapSerializables.toList()[1], + CustomMapSerialization({'z': 'zzz'})); + expect( e.mapOfNullableDates, equals({ @@ -222,6 +276,12 @@ void main() { 'a': {null, 3}, 'b': null, }); + expect(e.mapOfCustomStringSerializables['a'], null); + expect(e.mapOfCustomStringSerializables['b'], + CustomStringSerialization('hi')); + expect(e.mapOfCustomMapSerializables['a'], null); + expect(e.mapOfCustomMapSerializables['b'], + CustomMapSerialization({'z': 'zzz'})); expect(e.toJson(), equals(json)); }); @@ -257,6 +317,12 @@ class A { final Set setOfSerializableField; final Map mapOfSerializableField; + + final CustomStringSerialization customStringSerializationField; + + final CustomMapSerialization customMapSerializationField; + + final CustomNullableStringSerialization customNullableStringSerialization; } @JsonCodable() @@ -278,6 +344,10 @@ class B { final Set? nullableSetOfSerializableField; final Map? nullableMapOfSerializableField; + + final CustomStringSerialization? nullableCustomStringSerializationField; + + final CustomMapSerialization? nullableCustomMapSerializationField; } @JsonCodable() @@ -300,6 +370,10 @@ class E { final List?> listOfNullableMapsOfNullableInts; + final List listOfCustomStringSerializables; + + final List listOfCustomMapSerializables; + final Set setOfNullableDates; final Set setOfNullableInts; @@ -308,6 +382,10 @@ class E { final Set?> setOfNullableMapsOfNullableInts; + final Set setOfCustomStringSerializables; + + final Set setOfCustomMapSerializables; + final Map mapOfNullableDates; final Map mapOfNullableInts; @@ -315,9 +393,69 @@ class E { final Map mapOfNullableSerializables; final Map?> mapOfNullableSetsOfNullableInts; + + final Map mapOfCustomStringSerializables; + + final Map mapOfCustomMapSerializables; } @JsonCodable() class F { final int fieldWithDollarSign$; } + +class CustomStringSerialization { + final String a; + + CustomStringSerialization(this.a); + + String toJson() => a; + + factory CustomStringSerialization.fromJson(String a) => + CustomStringSerialization(a); + + @override + bool operator ==(Object other) => + other is CustomStringSerialization && a == other.a; + + @override + int get hashCode => a.hashCode; +} + +class CustomNullableStringSerialization { + final String? a; + + CustomNullableStringSerialization(this.a); + + String? toJson() => a; + + factory CustomNullableStringSerialization.fromJson(String? a) => + CustomNullableStringSerialization(a); + + @override + bool operator ==(Object other) => + other is CustomNullableStringSerialization && a == other.a; + + @override + int get hashCode => a.hashCode; +} + +class CustomMapSerialization { + final Map a; + + CustomMapSerialization(this.a); + + Map toJson() => a; + + factory CustomMapSerialization.fromJson(Map a) => + CustomMapSerialization(a); + + @override + bool operator ==(Object other) { + return other is CustomMapSerialization && + a.toString() == other.a.toString(); + } + + @override + int get hashCode => a.hashCode; +}