如何序列化Dart增强型枚举中的所有字段?

d8tt03nd  于 2023-07-31  发布在  其他
关注(0)|答案(2)|浏览(98)

我使用的是Flutter Freezed包,而它又使用Dart json_serialize包。
我有一个Dart增强枚举,包含2个字段:

enum WorkingMode {
  mix(type: 1, name: "mix"),
  parallel(type: 2, name: "parallel"),
  sequential(type: 3, name: "sequential"),
  relay(type: 4, name: "relay"),
  free(type: 5, name: "free"),
  associative(type: 6, name: "associative");

  final int type;
  final String name;

  const WorkingMode({
    required this.type,
    required this.name,
  });
}

字符串
在这个@freezed类中引用的:

@freezed
class Test with _$Test {
  const factory Test({
    required List<WorkingMode> workingModes,
  }) = _Test;

  factory Test.fromJson(Map<String, Object?> json) => _$TestFromJson(json);
}


下面是fromJson()方法生成的代码:

_$_Test _$$_TestFromJson(Map<String, dynamic> json) => _$_Test(
      workingModes: (json['workingModes'] as List<dynamic>)
          .map((e) => $enumDecode(_$WorkingModeEnumMap, e))
          .toList(),
    );

const _$WorkingModeEnumMap = {
  WorkingMode.mix: 'mix',
};


该代码期望仅基于一个特定字段(在本例中为name字段)解码最终值。我的意思是,如果收到的JSON是这样的:

{
   "workingModes":[
        "parallel",
        "sequential",
        "mix",
        "relay",
        "free",
        "associative"
   ]
}


但我遇到的问题是,我从Java REST API接收到的JSON以及枚举中的所有字段,如下所示:

{
   "workingModes":[
      {
         "type":2,
         "name":"parallel"
      },
      {
         "type":3,
         "name":"sequential"
      },
      {
         "type":1,
         "name":"mix"
      },
      {
         "type":4,
         "name":"relay"
      },
      {
         "type":5,
         "name":"free"
      },
      {
         "type":6,
         "name":"associative"
      }
   ]
}


我尝试使用@JsonValue@JsonEnum(valueField: 'XXX )注解,但似乎您可以指定另一个不同的字段,但不能同时指定几个字段。
现在我已经通过@JsonKey编写自己的fromJson/toJson方法来解决这个问题:

@freezed
class Test with _$Test {
  const factory Test({
    @JsonKey(
      fromJson: fromJsonWorkingModes,
      toJson: toJsonWorkingModes,
    )
    required List<WorkingMode> workingModes,
  }) = _Test;

  factory Test.fromJson(Map<String, Object?> json) => _$TestFromJson(json);
}

List<WorkingMode> fromJsonWorkingModes(List<dynamic> rawWorkingModes) {
  return rawWorkingModes
      .map((rawWorkingMode) => WorkingMode.values
          .firstWhere((element) => element.type == rawWorkingMode['type']))
      .toList();
}

List<dynamic> toJsonWorkingModes(List<WorkingMode> workingModes) {
  List<dynamic> finalWorkingModes = [];
  for (WorkingMode workingMode in workingModes) {
    var data = {'type': workingMode.type, 'name': workingMode.name};
    finalWorkingModes.add(data);
  }
  return finalWorkingModes;
}


但我想知道这是否可以以更直接的形式进行。当然,另一种可能性是去掉增强的枚举,并将其转换成一个普通类,也用@freezed注解,但这样我就失去了枚举的所有好处。
谢谢

vshtjzan

vshtjzan1#

目前我还不能对此进行测试,但似乎预期的方法是使用JsonEnum装饰器来指定要表示序列化枚举的字段。

@JsonEnum(valueField: 'name')
enum WorkingMode {
  mix(type: 1, name: "mix"),
  parallel(type: 2, name: "parallel"),
  sequential(type: 3, name: "sequential"),
  relay(type: 4, name: "relay"),
  free(type: 5, name: "free"),
  associative(type: 6, name: "associative");

  final int type;
  final String name;

  const WorkingMode({
    required this.type,
    required this.name,
  });
}

字符串

efzxgjgh

efzxgjgh2#

正如@Abion47所指出的,源问题是我没有从REST API点接收到严格的枚举对象。
这个问题至少有两种可能的解决方案。
第一个是通过@JsonKey使用带有自定义fromJson/toJson字段的枚举,如原始帖子中所述。
另一种解决方案是使用密封类。我创建了密封类,如下所示。请注意,对于我的特定情况,使用@Freezed(unionKey: 'name')@FreezedUnionValue来适应JSON中接收到的名称。

@Freezed(unionKey: 'name')
sealed class WorkingModeSealed with _$WorkingModeSealed {
  @FreezedUnionValue('property.working.mode.mix')
  factory WorkingModeSealed.mix(int type, String name) = MixMode;
  @FreezedUnionValue('property.working.mode.parallel')
  factory WorkingModeSealed.parallel(int type, String name) = ParallelMode;
  @FreezedUnionValue('property.working.mode.sequential')
  factory WorkingModeSealed.sequential(int type, String name) = SequentialMode;
  @FreezedUnionValue('property.working.mode.relay')
  factory WorkingModeSealed.relay(int type, String name) = RelayMode;
  @FreezedUnionValue('property.working.mode.free')
  factory WorkingModeSealed.free(int type, String name) = FreeMode;
  @FreezedUnionValue('property.working.mode.associative')
  factory WorkingModeSealed.associative(int type, String name) =
      AssociativeMode;

  factory WorkingModeSealed.fromJson(Map<String, Object?> json) =>
      _$WorkingModeSealedFromJson(json);
}

字符串
和反序列化JSON的基类如下:

@freezed
class Test with _$Test {
  const factory Test({
    required List<WorkingModeSealed> workingModesSealed,
  }) = _Test;

  factory Test.fromJson(Map<String, Object?> json) => _$TestFromJson(json);
}

相关问题