Skip to content

Commit 2ff08f8

Browse files
authored
Update json_converter_helper.dart
[JsonConverter]: Support generators with constructor arguments Ex: DateTimeConverter(pattern: 'YYYY/MM/dd')
1 parent f7cb516 commit 2ff08f8

File tree

1 file changed

+128
-79
lines changed

1 file changed

+128
-79
lines changed

json_serializable/lib/src/type_helpers/json_converter_helper.dart

+128-79
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ class JsonConverterHelper extends TypeHelper<TypeHelperContextWithConfig> {
2121

2222
@override
2323
Object? serialize(
24-
DartType targetType,
25-
String expression,
26-
TypeHelperContextWithConfig context,
27-
) {
24+
DartType targetType,
25+
String expression,
26+
TypeHelperContextWithConfig context,
27+
) {
2828
final converter = _typeConverter(targetType, context);
2929

3030
if (converter == null) {
@@ -54,11 +54,11 @@ Json? $converterToJsonName<Json, Value>(
5454

5555
@override
5656
Object? deserialize(
57-
DartType targetType,
58-
String expression,
59-
TypeHelperContextWithConfig context,
60-
bool defaultProvided,
61-
) {
57+
DartType targetType,
58+
String expression,
59+
TypeHelperContextWithConfig context,
60+
bool defaultProvided,
61+
) {
6262
final converter = _typeConverter(targetType, context);
6363
if (converter == null) {
6464
return null;
@@ -91,16 +91,14 @@ Value? $converterFromJsonName<Json, Value>(
9191
}
9292

9393
String _nullableJsonConverterLambdaResult(
94-
_JsonConvertData converter, {
95-
required String name,
96-
required DartType targetType,
97-
required String expression,
98-
required String callback,
99-
}) {
94+
_JsonConvertData converter, {
95+
required String name,
96+
required DartType targetType,
97+
required String expression,
98+
required String callback,
99+
}) {
100100
final jsonDisplayString = typeToCode(converter.jsonType);
101-
final fieldTypeDisplayString = converter.isGeneric
102-
? typeToCode(targetType)
103-
: typeToCode(converter.fieldType);
101+
final fieldTypeDisplayString = converter.isGeneric ? typeToCode(targetType) : typeToCode(converter.fieldType);
104102

105103
return '$name<$jsonDisplayString, $fieldTypeDisplayString>('
106104
'$expression, $callback)';
@@ -113,31 +111,31 @@ class _JsonConvertData {
113111
final bool isGeneric;
114112

115113
_JsonConvertData.className(
116-
String className,
117-
String accessor,
118-
this.jsonType,
119-
this.fieldType,
120-
) : accessString = 'const $className${_withAccessor(accessor)}()',
114+
String className,
115+
String? arguments,
116+
String accessor,
117+
this.jsonType,
118+
this.fieldType,
119+
) : accessString = 'const $className${_withAccessor(accessor)}(${arguments ?? ''})',
121120
isGeneric = false;
122121

123122
_JsonConvertData.genericClass(
124-
String className,
125-
String genericTypeArg,
126-
String accessor,
127-
this.jsonType,
128-
this.fieldType,
129-
) : accessString =
130-
'$className<$genericTypeArg>${_withAccessor(accessor)}()',
123+
String className,
124+
String? arguments,
125+
String genericTypeArg,
126+
String accessor,
127+
this.jsonType,
128+
this.fieldType,
129+
) : accessString = '$className<$genericTypeArg>${_withAccessor(accessor)}(${arguments ?? ''})',
131130
isGeneric = true;
132131

133132
_JsonConvertData.propertyAccess(
134-
this.accessString,
135-
this.jsonType,
136-
this.fieldType,
137-
) : isGeneric = false;
133+
this.accessString,
134+
this.jsonType,
135+
this.fieldType,
136+
) : isGeneric = false;
138137

139-
static String _withAccessor(String accessor) =>
140-
accessor.isEmpty ? '' : '.$accessor';
138+
static String _withAccessor(String accessor) => accessor.isEmpty ? '' : '.$accessor';
141139
}
142140

143141
/// If there is no converter for the params, return `null`.
@@ -146,9 +144,9 @@ class _JsonConvertData {
146144
///
147145
/// Used to make sure we create a smart encoding function.
148146
bool? hasConverterNullEncode(
149-
DartType targetType,
150-
TypeHelperContextWithConfig ctx,
151-
) {
147+
DartType targetType,
148+
TypeHelperContextWithConfig ctx,
149+
) {
152150
final data = _typeConverter(targetType, ctx);
153151

154152
if (data == null) {
@@ -159,34 +157,30 @@ bool? hasConverterNullEncode(
159157
}
160158

161159
_JsonConvertData? _typeConverter(
162-
DartType targetType,
163-
TypeHelperContextWithConfig ctx,
164-
) {
160+
DartType targetType,
161+
TypeHelperContextWithConfig ctx,
162+
) {
165163
List<_ConverterMatch> converterMatches(List<ElementAnnotation> items) => items
166164
.map(
167165
(annotation) => _compatibleMatch(
168-
targetType,
169-
annotation,
170-
annotation.computeConstantValue()!,
171-
),
172-
)
166+
targetType,
167+
annotation,
168+
annotation.computeConstantValue()!,
169+
),
170+
)
173171
.whereType<_ConverterMatch>()
174172
.toList();
175173

176174
var matchingAnnotations = converterMatches(ctx.fieldElement.metadata);
177175

178176
if (matchingAnnotations.isEmpty) {
179-
matchingAnnotations =
180-
converterMatches(ctx.fieldElement.getter?.metadata ?? []);
177+
matchingAnnotations = converterMatches(ctx.fieldElement.getter?.metadata ?? []);
181178

182179
if (matchingAnnotations.isEmpty) {
183180
matchingAnnotations = converterMatches(ctx.classElement.metadata);
184181

185182
if (matchingAnnotations.isEmpty) {
186-
matchingAnnotations = ctx.config.converters
187-
.map((e) => _compatibleMatch(targetType, null, e))
188-
.whereType<_ConverterMatch>()
189-
.toList();
183+
matchingAnnotations = ctx.config.converters.map((e) => _compatibleMatch(targetType, null, e)).whereType<_ConverterMatch>().toList();
190184
}
191185
}
192186
}
@@ -195,9 +189,9 @@ _JsonConvertData? _typeConverter(
195189
}
196190

197191
_JsonConvertData? _typeConverterFrom(
198-
List<_ConverterMatch> matchingAnnotations,
199-
DartType targetType,
200-
) {
192+
List<_ConverterMatch> matchingAnnotations,
193+
DartType targetType,
194+
) {
201195
if (matchingAnnotations.isEmpty) {
202196
return null;
203197
}
@@ -230,18 +224,13 @@ _JsonConvertData? _typeConverterFrom(
230224
}
231225

232226
final reviver = ConstantReader(match.annotation).revive();
233-
234-
if (reviver.namedArguments.isNotEmpty ||
235-
reviver.positionalArguments.isNotEmpty) {
236-
throw InvalidGenerationSourceError(
237-
'Generators with constructor arguments are not supported.',
238-
element: match.elementAnnotation?.element,
239-
);
240-
}
227+
// Support generators with constructor arguments
228+
String? arguments = _argsFromRevivable(reviver);
241229

242230
if (match.genericTypeArg != null) {
243231
return _JsonConvertData.genericClass(
244232
match.annotation.type!.element!.name!,
233+
arguments,
245234
match.genericTypeArg!,
246235
reviver.accessor,
247236
match.jsonType,
@@ -251,12 +240,73 @@ _JsonConvertData? _typeConverterFrom(
251240

252241
return _JsonConvertData.className(
253242
match.annotation.type!.element!.name!,
243+
arguments,
254244
reviver.accessor,
255245
match.jsonType,
256246
match.fieldType,
257247
);
258248
}
259249

250+
String? _argsFromRevivable(Revivable reviver) {
251+
String? arguments, positionalArguments, namedArguments;
252+
if (reviver.positionalArguments.isNotEmpty) {
253+
positionalArguments = reviver.positionalArguments.map((DartObject arg) => _argumentValueFromDartObject(arg)).join(', ');
254+
}
255+
if (reviver.namedArguments.isNotEmpty) {
256+
namedArguments = reviver.namedArguments.keys
257+
.map((String key) {
258+
dynamic arg = _argumentValueFromDartObject(reviver.namedArguments[key]);
259+
if (null != arg) return '$key: $arg';
260+
return null;
261+
})
262+
.where(_isNotEmpty)
263+
.join(', ');
264+
}
265+
arguments = <String?>[positionalArguments, namedArguments].where(_isNotEmpty).join(', ');
266+
return arguments;
267+
}
268+
269+
bool _isNotEmpty(dynamic element) => null != element;
270+
271+
dynamic _argumentValueFromDartObject(DartObject? obj) {
272+
try {
273+
if (null != obj) {
274+
if (obj.type?.isDartCoreString == true) {
275+
return '\'${obj.toStringValue()}\'';
276+
} else if (obj.type?.isDartCoreInt == true) {
277+
return obj.toIntValue();
278+
} else if (obj.type?.isDartCoreDouble == true) {
279+
return obj.toDoubleValue();
280+
} else if (obj.type?.isDartCoreBool == true) {
281+
return obj.toBoolValue();
282+
} else if (obj.type?.isDartCoreIterable == true || obj.type?.isDartCoreList == true) {
283+
return obj.toListValue();
284+
} else if (obj.type?.isDartCoreMap == true) {
285+
return obj.toMapValue();
286+
} else if (obj.type?.isDartCoreSet == true) {
287+
return obj.toSetValue();
288+
} else if (obj.type?.isDartCoreSymbol == true) {
289+
return obj.toSymbolValue();
290+
} else {
291+
ExecutableElement? executable = obj.toFunctionValue();
292+
if (null != executable) {
293+
return executable.displayName;
294+
} else if (null != obj.type) {
295+
String? typeDisplayString = obj.type?.getDisplayString(withNullability: false);
296+
if (null != typeDisplayString && typeDisplayString != 'Null') {
297+
final reviver = ConstantReader(obj).revive();
298+
String? arguments = _argsFromRevivable(reviver);
299+
return '$typeDisplayString(${arguments ?? ''})';
300+
}
301+
}
302+
}
303+
}
304+
} catch (e) {
305+
print(e);
306+
}
307+
return null;
308+
}
309+
260310
class _ConverterMatch {
261311
final DartObject annotation;
262312
final DartType fieldType;
@@ -265,24 +315,23 @@ class _ConverterMatch {
265315
final String? genericTypeArg;
266316

267317
_ConverterMatch(
268-
this.elementAnnotation,
269-
this.annotation,
270-
this.jsonType,
271-
this.genericTypeArg,
272-
this.fieldType,
273-
);
318+
this.elementAnnotation,
319+
this.annotation,
320+
this.jsonType,
321+
this.genericTypeArg,
322+
this.fieldType,
323+
);
274324
}
275325

276326
_ConverterMatch? _compatibleMatch(
277-
DartType targetType,
278-
ElementAnnotation? annotation,
279-
DartObject constantValue,
280-
) {
327+
DartType targetType,
328+
ElementAnnotation? annotation,
329+
DartObject constantValue,
330+
) {
281331
final converterClassElement = constantValue.type!.element as ClassElement;
282332

283-
final jsonConverterSuper =
284-
converterClassElement.allSupertypes.singleWhereOrNull(
285-
(e) => _jsonConverterChecker.isExactly(e.element),
333+
final jsonConverterSuper = converterClassElement.allSupertypes.singleWhereOrNull(
334+
(e) => _jsonConverterChecker.isExactly(e.element),
286335
);
287336

288337
if (jsonConverterSuper == null) {
@@ -311,8 +360,8 @@ _ConverterMatch? _compatibleMatch(
311360
if (converterClassElement.typeParameters.length > 1) {
312361
throw InvalidGenerationSourceError(
313362
'`JsonConverter` implementations can have no more than one type '
314-
'argument. `${converterClassElement.name}` has '
315-
'${converterClassElement.typeParameters.length}.',
363+
'argument. `${converterClassElement.name}` has '
364+
'${converterClassElement.typeParameters.length}.',
316365
element: converterClassElement);
317366
}
318367

0 commit comments

Comments
 (0)