【问题标题】:Encode Map with Enum to JSON使用 Enum 将 Map 编码为 JSON
【发布时间】:2021-06-06 08:12:02
【问题描述】:

我想将我的地图编码为 json。它看起来像这样:

Map<MyEnum, int> map = {type: limit};

MyEnum 是一个枚举。一个简单的json.encode(map) 不起作用,因为它不知道如何序列化我猜的枚举类。

错误信息是:

Unhandled Exception: Converting object to an encodable object failed: _LinkedHashMap len:1

我怎样才能设法将此映射序列化为 json?

【问题讨论】:

  • enum 应该翻译成什么?
  • @julemand101 最佳情况成字符串并解码回枚举

标签: json flutter dart serialization


【解决方案1】:

你可以在foundation.dart中使用describeEnum方法

【讨论】:

【解决方案2】:

这确实不是我推荐的解决方案,但我最终这样做主要是为了“好玩”。除了它很可怕之外,我不保证解决方案的任何内容。 :)

问题是enum 没有被定义为 Json 的有效类型,所以整个概念确实给我们带来了一些问题。一种解决方案是将enum 值转换为String,首先使用枚举的名称,然后是值的名称,例如MyFirstEnum.first1。如果在 enum 值上调用 toString(),Dart 会给出这种表示形式。

这很好,但为了安全起见,我们还可以在开头添加一个魔术字符串,如 DART_ENUM:MyFirstEnum.first1,这样更容易在其他字符串之间识别,这些字符串可能与 enum 值同名而不是枚举。

接下来是类型安全。在 Json 中,我们知道所有的地图都有String 作为键的类型。通过制作我们自己的enum 表示并允许它也是键,我们不能期望解码器返回例如Map&lt;String, dynamic&gt; 但必须返回 Map&lt;dynamic, dynamic&gt;

话虽如此,我尝试构建一个处理enum 值的Json 解码器和编码器。它也适用于地图中的enum 键:

import 'dart:convert';

class JsonConverterWithEnumSupport {
  final String magicString;
  final Set<Object> allEnumValues = {};
  final Map<String, Object> enumStringToEnumValue = {};

  JsonConverterWithEnumSupport(List<List<Object>>? enumsValues,
      {this.magicString = "DART_ENUM:"}) {
    enumsValues?.forEach(addEnumValues);
  }

  void addEnumValues(List<Object> enumValues) {
    for (final enumValue in enumValues) {
      enumStringToEnumValue[enumValue.toString()] = enumValue;
      allEnumValues.add(enumValue);
    }
  }

  String _addMagic(dynamic enumValue) => '$magicString$enumValue';
  String _removeMagic(String string) => string.substring(magicString.length);

  String encode(Object? value) =>
      json.encode(value, toEncodable: (dynamic object) {
        if (object is Map) {
          return object.map<dynamic, dynamic>((dynamic key, dynamic value) =>
              MapEntry<dynamic, dynamic>(
                  allEnumValues.contains(key) ? _addMagic(key) : key,
                  allEnumValues.contains(value) ? _addMagic(value) : value));
        }

        if (object is List) {
          return object.map<dynamic>(
              (dynamic e) => allEnumValues.contains(e) ? _addMagic(e) : e);
        }

        if (allEnumValues.contains(object)) {
          return _addMagic(object);
        }

        return object;
      });

  dynamic decode(String source) => json.decode(source, reviver: (key, value) {
        if (value is String && value.startsWith(magicString)) {
          return enumStringToEnumValue[_removeMagic(value)];
        }

        if (value is Map) {
          return value.map<dynamic, dynamic>((dynamic key, dynamic value) =>
              MapEntry<dynamic, dynamic>(
                  (key is String) && key.startsWith(magicString)
                      ? enumStringToEnumValue[_removeMagic(key)]
                      : key,
                  value));
        }

        return value;
      });
}

enum MyFirstEnum { first1, first2 }
enum MySecondEnum { second1, second2 }

void main() {
  final converter =
      JsonConverterWithEnumSupport([MyFirstEnum.values, MySecondEnum.values]);

  final jsonString = converter.encode({
    MyFirstEnum.first1: [MySecondEnum.second2, MySecondEnum.second1],
    'test': {MyFirstEnum.first2: 5}
  });

  print(jsonString);
  // {"DART_ENUM:MyFirstEnum.first1":["DART_ENUM:MySecondEnum.second2","DART_ENUM:MySecondEnum.second1"],"test":{"DART_ENUM:MyFirstEnum.first2":5}}
  print(converter.decode(jsonString));
  // {MyFirstEnum.first1: [MySecondEnum.second2, MySecondEnum.second1], test: {MyFirstEnum.first2: 5}}
}

您需要将所有可能的enum 值输入JsonConverterWithEnumSupport(例如,请参阅底部的main 方法)。

如果您不想在每个 enum 上附加魔术字符串,您可以创建 JsonConverterWithEnumSupport 并使用:magicString: '' 作为参数。

【讨论】:

    【解决方案3】:

    您可以在枚举上创建一个扩展以将其转换为字符串,然后将您的地图转换为 Map&lt;String, int&gt;,以便正确编码:

    import 'dart:convert';
    
    enum MyEnum { type }
    
    extension MyEnumModifier on MyEnum {
      String get string => this.toString().split('.').last;
    }
    
    void main() {
      Map<MyEnum, int> map = {MyEnum.type: 10};
      Map<String, int> newMap = {};
      
      map.forEach((key, value) =>
        newMap[key.string] = value);
      
      final json = jsonEncode(newMap);
      print(json);
    }
    

    输出

    {"type":10}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-02
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      • 2017-03-04
      • 2015-11-11
      • 2018-12-24
      相关资源
      最近更新 更多