createDeserializer method

Mapper createDeserializer()

Implementation

Mapper createDeserializer() {
  Map<Type, MappingDefinition> mappings = {};
  var queue = Queue<Type>.from([type]);

  // local function

  MappingDefinition process(Type type) {
    var typeDescriptor = TypeDescriptor.forType(type);

    var jsonSerializable = typeDescriptor.findAnnotation<JsonSerializable>() ?? JsonSerializable();

    MappingDefinition typeMapping;

    if (jsonSerializable.discriminatorField.isNotEmpty)
      typeMapping = PolymorphicMappingDefinition(field:  jsonSerializable.discriminatorField, targetClass: type);
    else
      typeMapping = MappingDefinition(sourceClass: Map<String, dynamic>, targetClass: type);

    mappings[type] = typeMapping;

    // local function

    void check(Type type) {
      if ( !TypeDescriptor.hasType(type))
        return;

      if (!mappings.containsKey(type)) {
        queue.add(type);

        // check all subclasses as well

        for ( var sub in TypeDescriptor.forType(type).childClasses)
          check(sub.type);
      }
    }

    for ( var sub in TypeDescriptor.forType(type).childClasses)
      check(sub.type);

    // process fields

    for ( var field in typeDescriptor.getFields()) {
      var json = field.findAnnotation<Json>() ?? Json(name: field.name, defaultValue: null);

      var includeNull = jsonSerializable.includeNull && json.includeNull != false;

      if ( json.ignore)
        continue;

      var jsonField = json.name;
      if (jsonField.isEmpty)
        jsonField = field.name;

      Object? defaultValue = JSONAccessor;
      if ( !json.required) {
        defaultValue = json.defaultValue;

        //if ( !field.type.isValid(defaultValue))
        //  throw MapperException("the default $defaultValue for ${field.name} is not valid"); // TODO?
      }

      if (field.type is ListType) {
        var elementType = field.elementType;

        check(elementType!);

        typeMapping.map(
            from: JSONAccessor(
                name: jsonField,
                type: List<dynamic>,
                includeNull: includeNull,
                defaultValue: defaultValue,
                containerConstructor: () => []
            ),
            to: field.name,
            deep: true,
            validate: validate
        );
      }
      else if ( field.type is ObjectType) {
        var objectType = field.type as ObjectType;

        Convert? convert ;
        if ( json.converter != null) {
          convert = JSON.getConvert4(json.converter!);
        }
        else convert = JSON.instance.getConvert(field.type.type);

        if ( !objectType.typeDescriptor.isEnum() && convert == null) {
          var target = objectType.type;

          check(target);

          typeMapping.map(
              from: JSONAccessor(
                  name: jsonField,
                  type: Map<String, dynamic>,
                  includeNull: includeNull,
                  defaultValue: defaultValue
              ),
              //validate: validate,
              to: field.name,
              deep: true
          );
        }
        else {
          // manual converter?

          typeMapping.map(
              //convert: convert,
              from: JSONAccessor(
                  name: jsonField,
                  type: field.type.type,
                  convert: convert?.targetConverter(),
                  includeNull: includeNull,
                  defaultValue: defaultValue,
              ),
              to: field.name,
              deep: convert == null,
              validate: validate
          );
        }
      } // if
      else {
        Convert? convert ;
        if ( json.converter != null) {
          convert = JSON.getConvert4(json.converter!);
        }
        else convert = JSON.instance.getConvert(field.type.type);

        typeMapping.map(
            from: JSONAccessor(
                name: jsonField,
                type: field.type.type,
                convert: convert?.targetConverter(),
                includeNull: includeNull,
                defaultValue: defaultValue
            ),
            to: field.name,
            //convert: convert,
            validate: validate
        );
      }
    }

    return typeMapping;
  }

  // work on queue

  while (queue.isNotEmpty) {
    process(queue.removeFirst());
  }

  // done

  var mapper = Mapper(mappings.values.toList());

  deserializerMapping = mapper.mappings.values.firstWhere((mapping) => mapping.typeDescriptor.type == type);

  return mapper;
}