deserialize method
Returns Dart code that deserializes an expression
representing a JSON
literal to into targetType
.
If targetType
is not supported, returns null
.
Let's say you want to deserialize a class Foo
by taking an int
stored
in a JSON literal and calling the Foo.fromInt
constructor.
Treating expression
as a opaque Dart expression representing a JSON
literal, the deserialize implementation could be a simple as:
String deserialize(DartType targetType, String expression) =>
"new Foo.fromInt($expression)";
```.
Note that [targetType] is not used here. If you wanted to support many
types of [targetType] you could write:
```dart
String deserialize(DartType targetType, String expression) =>
"new ${targetType.name}.fromInt($expression)";
```.
Implementation
@override
Object? deserialize(DartType targetType, String expression,
TypeHelperContextWithConfig context, bool defaultProvided) {
if (!typeChecker.isExactlyType(targetType)) {
return null;
}
final typeArgs = targetType.typeArgumentsOf(typeChecker)!;
assert(typeArgs.length == 2);
final keyArg = typeArgs.first;
final valueArg = typeArgs.last;
final keyArgAsGenericString =
keyArg.getDisplayString(withNullability: true);
final valueArgAsGenericString =
valueArg.getDisplayString(withNullability: true);
checkSafeKeyType(expression, keyArg);
final valueArgIsAny = valueArg.isLikeDynamic;
final keyStringable = isKeyStringable(keyArg);
final targetTypeIsNullable = targetType.isNullableType || defaultProvided;
final anyMap = context.config.anyMap;
final optionalQuestion = targetTypeIsNullable ? '?' : '';
if (!keyStringable) {
if (valueArgIsAny) {
if (anyMap) {
if (keyArg.isLikeDynamic) {
return wrapNullableIfNecessary(
expression,
deserializeFromMapExpression(
'($expression as Map$optionalQuestion)', keyArg, valueArg),
targetTypeIsNullable);
}
} else {
// this is the trivial case. Do a runtime cast to the known type of JSON
// map values - `Map<String, dynamic>`
return wrapNullableIfNecessary(
expression,
deserializeFromMapExpression(
'($expression as Map<String, dynamic>)', keyArg, valueArg),
targetTypeIsNullable);
}
}
if (!targetTypeIsNullable &&
(valueArgIsAny ||
simpleJsonTypeChecker.isAssignableFromType(valueArg))) {
// No mapping of the values or null check required!
return wrapNullableIfNecessary(
expression,
deserializeFromMapExpression(
'Map<String, $valueArgAsGenericString>.from($expression as Map)',
keyArg,
valueArg),
targetTypeIsNullable);
}
}
// In this case, we're going to create a new Map with matching reified
// types.
var itemSubVal = context.deserialize(valueArg, closureArg).toString();
itemSubVal = wrapNullableIfNecessary(closureArg, itemSubVal, valueArg.isNullableType);
final mapCast = anyMap ? 'as Map' : 'as Map<String, dynamic>';
String keyUsage;
if (keyArg.isEnum) {
keyUsage = context.deserialize(keyArg, _keyParam).toString();
} else if (context.config.anyMap &&
!(keyArg.isDartCoreObject || keyArg.isDynamic)) {
keyUsage = '$_keyParam as String';
} else if (context.config.anyMap &&
keyArg.isDartCoreObject &&
!keyArg.isNullableType) {
keyUsage = '$_keyParam as Object';
} else {
keyUsage = _keyParam;
}
final toFromString = forType(keyArg);
if (toFromString != null) {
keyUsage = toFromString.deserialize(keyArg, keyUsage, false, true)!;
}
return wrapNullableIfNecessary(
expression,
deserializeFromMapExpression(
'($expression $mapCast).map('
'($keyParam, $closureArg) => MapEntry($keyUsage, $itemSubVal),)',
keyArg,
valueArg),
targetTypeIsNullable);
}