dart_mappable icon indicating copy to clipboard operation
dart_mappable copied to clipboard

bug: enum field default cannot be null

Open lukepighetti opened this issue 1 year ago • 4 comments

@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 avatar Aug 02 '24 18:08 lukepighetti

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;
  }
}

lukepighetti avatar Aug 02 '24 18:08 lukepighetti

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?

schultek avatar Aug 04 '24 08:08 schultek

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 avatar Sep 09 '24 11:09 RohanSenguptaMantisPro

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.

RohanSenguptaMantisPro avatar Sep 10 '24 07:09 RohanSenguptaMantisPro

Hi @schultek, yes, this should be a feature request, bc now when trying to parse this enum, I get "MapperException: Failed to decode (TaskTemplate).widget(TaskWidgetEnum?)[someWidget]: No enum value matches 'someWidget', did you use the correct mode or case-style or forgot to specify a default value?". "someWidget" is a new unknown value coming from API

@HiveType(typeId: )
@MappableEnum(defaultValue: null) <-- this
enum TaskWidgetEnum {
  @MappableValue('TASK')
  @HiveField(0)
  task,
  @MappableValue('FORM')
  @HiveField(1)
  form,
}
final TaskWidgetEnum? taskWidgetEnum;

msxenon avatar Jul 24 '25 13:07 msxenon