schultek / dart_mappable

Improved json serialization and data classes with full support for generics, inheritance, customization and more.
https://pub.dev/packages/dart_mappable
MIT License
135 stars 20 forks source link

excepion when using `MappingHook` on nullable fields: type `Null` is not a subtype of type `X` in type cast #204

Closed MohaAmiry closed 4 days ago

MohaAmiry commented 2 weeks ago

for the following code:

class LocationHook extends MappingHook{

  @override
  Location? afterDecode(Object? value) {
    print(value);
    if(value is! Location) return null;
    return (value).address.isEmpty? null: value;
  }

  const LocationHook();
}

@MappableClass(hook: LocationHook())
class Location with LocationMappable{
  final String address;
  final String? details;

  const Location(this.address, this.details);
}

@MappableClass()
class Person with PersonMappable{
  final String name;
  final Location? location;

  const Person(this.name, this.location);
}

void main() async {
var map = {"name":"name","location":{"address":"","details":null}};
print(PersonMapper.fromMap(map));
}

Expected Result: i'm expecting to change every Location object to null in case the address is empty.

Actual Result: running the previous code will throw the following exception although the Person.location is nullable:

MapperException: Failed to decode (Person).location(Location?): type 'Null' is not a subtype of type 'Location' in type cast
#0      InterfaceMapperBase.decoder (package:dart_mappable/src/mappers/interface_mapper.dart:141:42)
#1      ClassMapperBase.decoder (package:dart_mappable/src/mappers/class_mapper.dart:153:24)
#2      MapperBase.decodeValue (package:dart_mappable/src/mappers/mapper_base.dart:43:14)
#3      _MapperContainerBase.fromValue (package:dart_mappable/src/mapper_container.dart:369:21)
#4      DecodingUtil.$dec (package:dart_mappable/src/mapper_utils.dart:24:29)
#5      Field.decode (package:dart_mappable/src/mappers/interface_mapper.dart:86:19)
#6      DecodingData.dec (package:dart_mappable/src/mappers/interface_mapper.dart:100:26)
#7      PersonMapper._instantiate (package:lab/lab.mapper.dart:148:43)
#8      InterfaceMapperBase.decode (package:dart_mappable/src/mappers/interface_mapper.dart:153:25)
#9      ClassMapperBase.decode (package:dart_mappable/src/mappers/class_mapper.dart:176:18)
#10     InterfaceMapperBase.decoder (package:dart_mappable/src/mappers/interface_mapper.dart:138:18)
#11     ClassMapperBase.decoder (package:dart_mappable/src/mappers/class_mapper.dart:153:24)
#12     MapperBase.decodeValue (package:dart_mappable/src/mappers/mapper_base.dart:43:14)
#13     InterfaceMapperBase.decodeMap (package:dart_mappable/src/mappers/interface_mapper.dart:205:47)
#14     PersonMapper.fromMap (package:lab/lab.mapper.dart:155:32)
#15     main (package:lab/lab.dart:34:20)
#16     _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#17     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
schultek commented 4 days ago

Yes applying a hook to a class will always expect the non-nullable type (Location). You can try instead applying the hook to the field, which will the work correctly with the nullable type (Location?).

So:

@MappableClass()
class Person with PersonMappable{
  final String name;
  @MappableField(hook: LocationHook())
  final Location? location;

  const Person(this.name, this.location);
}
MohaAmiry commented 4 days ago

so to achieve my goal "make every location null if the a specific condition is true", i must use the location hook everywhere i use the location object. right?

schultek commented 4 days ago

yes