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
165 stars 23 forks source link

bug: enum field default cannot be null #217

Open lukepighetti opened 3 months ago

lukepighetti commented 3 months ago
@MappableClass()
class PbUserModel with PbUserModelMappable {
  final PbUserModelAvatar? avatar;

  PbUserModel({
    required this.avatar,
  });
}

@MappableEnum(mode: ValuesMode.indexed)
enum PbUserModelAvatar {
  one,
  two,
  three,
  ;
}

when you try to deserialize with PbUserModelMapper.fromJson when avatar is "", you get this error

MapperException: Failed to decode (PbUserModel).avatar(PbUserModelAvatar?)[]: No enum value matches '', did you use the correct mode or case-style or forgot to specify a default value?

but you cannot specify that the default value should be null

also EmptyToNullHook doesn't appear to work. my only guess is that it is of type String? instead of String

lukepighetti commented 3 months ago

ah it looks like EmptyToNullHook operates on the final result, not the json. ended up having to make this

import 'package:dart_mappable/dart_mappable.dart';

class NullableEnumHook extends MappingHook {
  const NullableEnumHook();
  @override
  dynamic beforeDecode(dynamic value) {
    return value is String && value.isEmpty ? null : value;
  }
}
schultek commented 3 months ago

This is the correct approach, since a enums default value cannot be null (since it must be a value of the enum).

Or is this a feature request?

RohanSenguptaMantisPro commented 2 months ago

ah it looks like EmptyToNullHook operates on the final result, not the json. ended up having to make this

import 'package:dart_mappable/dart_mappable.dart';

class NullableEnumHook extends MappingHook {
  const NullableEnumHook();
  @override
  dynamic beforeDecode(dynamic value) {
    return value is String && value.isEmpty ? null : value;
  }
}

Indeed this is the way to do it. As mentioned in the docs too : https://pub.dev/documentation/dart_mappable/latest/topics/Mapping%20Hooks-topic.html .

Also could you please elaborate on why the inbuilt EmptyToNullHook is not working and we have to implement a custom one.

but @lukepighetti can I use this nullableHook for other data types too right ? Not only for the enums. If there are int,double fields and json response has empty string values, the same problem will occur with dart_mappable right?

So I can implement the same solution I suppose? and in that case I would use @MappableField(hooks: [NullableHook()]) to all the properties I want to.

Is that right? @lukepighetti

RohanSenguptaMantisPro commented 2 months ago

ah it looks like EmptyToNullHook operates on the final result, not the json. ended up having to make this

import 'package:dart_mappable/dart_mappable.dart';

class NullableEnumHook extends MappingHook {
  const NullableEnumHook();
  @override
  dynamic beforeDecode(dynamic value) {
    return value is String && value.isEmpty ? null : value;
  }
}

Indeed this is the way to do it. As mentioned in the docs too : https://pub.dev/documentation/dart_mappable/latest/topics/Mapping%20Hooks-topic.html .

but @lukepighetti can I use this nullableHook for other data types too right ? Not only for the enums. If there are int,double fields and json response has empty string values, the same problem will occur with dart_mappable right?

So I can implement the same solution I suppose? and in that case I would use @MappableField(hooks: [NullableHook()]) to all the properties I want to.

Is that right? @lukepighetti

But isn't there a more 'DRY' approach ? else we would have to apply the hooks to all the properties of the class requiring the hook.

I thought applying the hook to whole class using @MappableClass(hook: ) would apply the hook to all the properties of the class before deserializing individual properties, but it doesn't work like that. It takes the whole class as a map in the value parameter of beforeDecode method. so it doesn't apply the hook before deserializing individual properties of the class.