【问题标题】:Dart json serializable - decode value as different typeDart json可序列化 - 将值解码为不同类型
【发布时间】:2022-11-10 13:41:38
【问题描述】:

假设我正在构建一个接收 json 作为响应的应用程序,例如:

{
  "a": 5,
  "b": [1, 2, 3]
}

假设我想将此 json 解析为以下类:

class Example {
  String a;
  List<String> b;
}

使用 Dart 的 json_serializable 包我可以执行以下操作

String intToString(int value) => value.toString();
List<String> intToStringList(List<int> value) => value.map(intToString).toList();

@JsonSerializable()
class Example {
  @JsonKey(fromJson: intToString)
  String a;
  @JsonKey(fromJson: intToStringList)
  List<String> b;

  Example(this.a, this.b);

  factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json);
  Map<String, dynamic> toJson() => _$ExampleToJson(this);
}

我的问题是,是否有任何内置的、更优雅的方法可以在没有自定义方法实现的情况下执行此操作。

【问题讨论】:

    标签: json dart json-deserialization


    【解决方案1】:

    我认为问题不在于生成器的实现。我尝试了另一个发电机,也没有其他办法。

    import 'dart:io';
    
    import 'package:object_serializer/json_serializer_generator.dart';
    import 'package:yaml/yaml.dart';
    
    void main() {
      final classes = loadYaml(_classes) as Map;
      final g = JsonSerializerGenerator();
      final classesCode = g.generateClasses(classes);
      final values = {
        'classes': classesCode,
      };
    
      var source = g.render(_template, values);
      source = g.format(source);
      File('bin/stackoverflow.dart').writeAsStringSync(source);
    }
    
    const _classes = r'''
    Example:
      fields:
        a:
          type: String
          deserialize: "(x){ return x == null ? '0' : '$x'; }"
        b:
          type: List<String>
          deserialize: "(x){ return x == null ? <String>[] : (x as List).map((e) => e == null ? '0' : '$x').toList(); }"
    ''';
    
    const _template = r'''
    {{classes}}
    ''';
    
    

    同样的问题。
    甚至需要更多代码,因为“反序列化”不是后处理,而是自定义反序列化处理的完整实现。

    class Example {
      Example({required this.a, required this.b});
    
      factory Example.fromJson(Map json) {
        return Example(
          a: (x) {
            return x == null ? '0' : '$x';
          }(json['a']),
          b: (x) {
            return x == null
                ? <String>[]
                : (x as List).map((e) => e == null ? '0' : '$x').toList();
          }(json['b']),
        );
      }
    
      final String a;
    
      final List<String> b;
    
      static List<Example> fromJsonList(List json) {
        return json.map((e) => Example.fromJson(e as Map)).toList();
      }
    
      Map<String, dynamic> toJson() {
        return {
          'a': a,
          'b': b,
        };
      }
    
      static List<Map<String, dynamic>> toJsonList(List<Example> list) {
        return list.map((e) => e.toJson()).toList();
      }
    }
    

    【讨论】:

      【解决方案2】:

      所以我发现这样做最干净的方法是使用JsonConverter

      class StringListFromIntListConverter implements JsonConverter<List<String>, List<int>> {
        const StringListFromIntListConverter();
      
        @override
        List<String> fromJson(List<int> json) => json.map((num) => num.toString()).toList()
      
        @override
        List<int> toJson(List<String> object) => object.map((str) => int.parse(str)).toList();
      }
      

      然后在需要的地方使用这个转换器,如下所示:

      @JsonSerializable()
      @StringListFromIntListConverter()
      class Example {
        String a;
        List<String> b;
      
        Example(this.a, this.b);
      
        factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json);
        Map<String, dynamic> toJson() => _$ExampleToJson(this);
      }
      

      这将自动反序列化此类中的字符串列表并将其序列化为 int 列表。 很好的是,这可以为每种类型实现并在不同的地方重用。

      发现这对于作为某种数字接收的时间和持续时间对象非常有用,我希望我的成员成为我需要的实际类型。

      【讨论】:

        猜你喜欢
        • 2019-01-27
        • 2022-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多