deserialize method

  1. @override
Object? deserialize(
  1. DartType targetType,
  2. String expression,
  3. TypeHelperContextWithConfig context,
  4. bool defaultProvided,
)
override

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 (targetType is! InterfaceType) {
    return null;
  }

  final classElement = targetType.element;

  var fromJsonCtor = classElement.constructors
      .where((ce) => ce.name == 'fromJson')
      .singleOrNull;

  var output = expression;
  if (fromJsonCtor != null) {
    while (fromJsonCtor!.enclosingElement is ClassElement &&
        (fromJsonCtor.enclosingElement as ClassElement).isMixinApplication &&
        fromJsonCtor.superConstructor != null) {
      fromJsonCtor = fromJsonCtor.superConstructor!.baseElement;
    }

    final positionalParams = fromJsonCtor.formalParameters
        .where((element) => element.isPositional)
        .toList();

    if (positionalParams.isEmpty) {
      throw InvalidGenerationSourceError(
        'Expecting a `fromJson` constructor with exactly one positional '
        'parameter. Found a constructor with 0 parameters.',
        element: fromJsonCtor,
      );
    }

    final instantiatedTarget =
        targetType.asInstanceOf(fromJsonCtor.enclosingElement) ?? targetType;
    final instantiatedCtor = instantiatedTarget.constructors
        .where((ce) => ce.baseElement == fromJsonCtor!.baseElement)
        .singleOrNull;
    final instantiatedPositionalParams = instantiatedCtor?.formalParameters
        .where((element) => element.isPositional)
        .toList();

    final asCastType =
        instantiatedPositionalParams != null &&
            instantiatedPositionalParams.isNotEmpty
        ? instantiatedPositionalParams.first.type
        : positionalParams.first.type;

    output = context.deserialize(asCastType, output).toString();

    final args = [
      output,
      ..._helperParams(
        context.deserialize,
        _decodeHelper,
        targetType,
        positionalParams.skip(1).where((e) => e.isRequiredPositional),
        fromJsonCtor,
      ),
    ];

    output = args.join(', ');
  } else if (_annotation(context.config, targetType)?.createFactory == true) {
    if (context.config.anyMap) {
      output += ' as Map';
    } else {
      output += ' as Map<String, dynamic>';
    }
  } else {
    return null;
  }

  // TODO: the type could be imported from a library with a prefix!
  // https://github.com/google/json_serializable.dart/issues/19
  final lambda = LambdaResult(
    output,
    '${typeToCode(targetType.promoteNonNullable())}.fromJson',
  );

  return DefaultContainer(expression, lambda);
}