diff --git a/pkg/json/CHANGELOG.md b/pkg/json/CHANGELOG.md index cfbb0bad53f8..b78404b21bc5 100644 --- a/pkg/json/CHANGELOG.md +++ b/pkg/json/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.20.3 + +- Add support for DateTime, serializing it to an ISO-8601 String. + # 0.20.2 - Fix generated code syntax error when defining fields containing the dollar sign `$` by using raw strings. diff --git a/pkg/json/README.md b/pkg/json/README.md index 05cd188dffde..a4ccd79d2cae 100644 --- a/pkg/json/README.md +++ b/pkg/json/README.md @@ -55,6 +55,8 @@ class Manager extends User { All native JSON types are supported (`int`, `double`, `String`, `bool`, `Null`). +Supports `DateTime`, serializing it to an ISO-8601 String. + The core collection types `List`, `Set`, and `Map` are also supported, if their elements are supported types. For elements which require more than just a cast, the type must be statically provided through a generic type argument on the diff --git a/pkg/json/lib/json.dart b/pkg/json/lib/json.dart index bcf237f7a7aa..1a2f4ddfdabf 100644 --- a/pkg/json/lib/json.dart +++ b/pkg/json/lib/json.dart @@ -378,6 +378,16 @@ mixin _FromJson on _Shared { ' as ', type.code, ]); + case 'DateTime': + return RawCode.fromParts([ + if (nullCheck != null) nullCheck, + await builder.resolveIdentifier(_dartCore, 'DateTime'), + '.parse(', + jsonReference, + ' as ', + introspectionData.stringCode, + ')' + ]); } } @@ -511,7 +521,9 @@ mixin _ToJson on _Shared { if (doNullCheck) '!', ]), builder, - introspectionData), + introspectionData, + // We already are doing the null check. + omitNullCheck: true), ';\n ', ]); if (doNullCheck) { @@ -570,12 +582,16 @@ mixin _ToJson on _Shared { } /// Returns a [Code] object which is an expression that converts an instance - /// of type [type] (referenced by [valueReference]) into a JSON map. + /// of type [rawType] (referenced by [valueReference]) into a JSON map. + /// + /// Null checks will be inserted if [rawType] is nullable, unless + /// [omitNullCheck] is `true`. Future _convertTypeToJson( TypeAnnotation rawType, Code valueReference, DefinitionBuilder builder, - _SharedIntrospectionData introspectionData) async { + _SharedIntrospectionData introspectionData, + {bool omitNullCheck = false}) async { final type = _checkNamedType(rawType, builder); if (type == null) { return RawCode.fromString( @@ -589,7 +605,7 @@ mixin _ToJson on _Shared { "throw 'Unable to serialize type ${type.code.debugString}'"); } - var nullCheck = type.isNullable + var nullCheck = type.isNullable && !omitNullCheck ? RawCode.fromParts([ valueReference, // `null` is a reserved word, we can just use it. @@ -624,6 +640,8 @@ mixin _ToJson on _Shared { ]); case 'int' || 'double' || 'num' || 'String' || 'bool': return valueReference; + case 'DateTime': + return RawCode.fromParts([if (nullCheck != null) nullCheck, valueReference, '.toIso8601String()']); } } diff --git a/pkg/json/pubspec.yaml b/pkg/json/pubspec.yaml index 98a28ee251f4..e2eb306c0d6b 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.2 +version: 0.20.3 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 b18c3320df33..15f48bc1ec4a 100644 --- a/pkg/json/test/json_codable_test.dart +++ b/pkg/json/test/json_codable_test.dart @@ -16,6 +16,7 @@ void main() { 'intField': 10, 'doubleField': 12.5, 'numField': 11, + 'dateTimeField': '2024-11-11T03:42:29.108308', 'listOfSerializableField': [ {'x': 1}, ], @@ -33,6 +34,7 @@ void main() { expect(a.intField, 10); expect(a.doubleField, 12.5); expect(a.numField, 11); + expect(a.dateTimeField, DateTime.parse('2024-11-11T03:42:29.108308')); expect(a.listOfSerializableField.single.x, 1); expect(a.setOfSerializableField.single.x, 2); expect(a.mapOfSerializableField['c']!.x, 3); @@ -47,6 +49,7 @@ void main() { 'nullableIntField': 9, 'nullableDoubleField': 11.5, 'nullableNumField': 11.1, + 'nullableDateTimeField': '2024-11-11T03:42:29.108308', 'nullableListOfSerializableField': [ {'x': 1}, ], @@ -64,6 +67,8 @@ void main() { expect(b.nullableIntField, 9); expect(b.nullableDoubleField, 11.5); expect(b.nullableNumField, 11.1); + expect(b.nullableDateTimeField, + DateTime.parse('2024-11-11T03:42:29.108308')); expect(b.nullableListOfSerializableField!.single.x, 1); expect(b.nullableSetOfSerializableField!.single.x, 2); expect(b.nullableMapOfSerializableField!['d']!.x, 3); @@ -78,6 +83,7 @@ void main() { 'nullableIntField': null, 'nullableDoubleField': null, 'nullableNumField': null, + 'nullableDateTimeField': null, 'nullableListOfSerializableField': null, 'nullableSetOfSerializableField': null, 'nullableMapOfSerializableField': null, @@ -87,6 +93,7 @@ void main() { expect(b.nullableIntField, null); expect(b.nullableDoubleField, null); expect(b.nullableNumField, null); + expect(b.nullableDateTimeField, null); expect(b.nullableListOfSerializableField, null); expect(b.nullableMapOfSerializableField, null); expect(b.nullableSetOfSerializableField, null); @@ -101,6 +108,7 @@ void main() { expect(b.nullableIntField, null); expect(b.nullableDoubleField, null); expect(b.nullableNumField, null); + expect(b.nullableDateTimeField, null); expect(b.nullableListOfSerializableField, null); expect(b.nullableMapOfSerializableField, null); expect(b.nullableSetOfSerializableField, null); @@ -122,6 +130,7 @@ void main() { test('collections of nullable objects', () { var json = { + 'listOfNullableDates': [null, '2024-11-11T03:42:29.108308'], 'listOfNullableInts': [null, 1], 'listOfNullableSerializables': [ {'x': 1}, @@ -131,6 +140,10 @@ void main() { null, {'a': 1, 'b': null}, ], + 'setOfNullableDates': [ + null, + '2024-11-12T03:42:29.108308', + ], 'setOfNullableInts': [ null, 2, @@ -146,6 +159,10 @@ void main() { 'b': null, }, ], + 'mapOfNullableDates': { + 'a': '2024-11-13T03:42:29.108308', + 'b': null, + }, 'mapOfNullableInts': { 'a': 3, 'b': null, @@ -161,6 +178,8 @@ void main() { }; var e = E.fromJson(json); + expect(e.listOfNullableDates, + equals([null, DateTime.parse('2024-11-11T03:42:29.108308')])); expect(e.listOfNullableInts, equals([null, 1])); expect(e.listOfNullableSerializables.first!.x, 1); expect(e.listOfNullableSerializables[1], null); @@ -170,6 +189,8 @@ void main() { null, {'a': 1, 'b': null}, ])); + expect(e.setOfNullableDates, + equals([null, DateTime.parse('2024-11-12T03:42:29.108308')])); expect(e.setOfNullableInts, equals({null, 2})); expect(e.setOfNullableSerializables.first!.x, 2); expect(e.setOfNullableSerializables.elementAt(1), null); @@ -182,6 +203,12 @@ void main() { 'b': null, }, })); + expect( + e.mapOfNullableDates, + equals({ + 'a': DateTime.parse('2024-11-13T03:42:29.108308'), + 'b': null, + })); expect( e.mapOfNullableInts, equals({ @@ -223,6 +250,8 @@ class A { final num numField; + final DateTime dateTimeField; + final List listOfSerializableField; final Set setOfSerializableField; @@ -242,6 +271,8 @@ class B { final num? nullableNumField; + final DateTime? nullableDateTimeField; + final List? nullableListOfSerializableField; final Set? nullableSetOfSerializableField; @@ -261,18 +292,24 @@ class D extends C { @JsonCodable() class E { + final List listOfNullableDates; + final List listOfNullableInts; final List listOfNullableSerializables; final List?> listOfNullableMapsOfNullableInts; + final Set setOfNullableDates; + final Set setOfNullableInts; final Set setOfNullableSerializables; final Set?> setOfNullableMapsOfNullableInts; + final Map mapOfNullableDates; + final Map mapOfNullableInts; final Map mapOfNullableSerializables;