【问题标题】:Deserialise JSON response from API into type in Dart将来自 API 的 JSON 响应反序列化为 Dart 中的类型
【发布时间】:2020-02-25 06:16:31
【问题描述】:

我想弄清楚这一点,因为这是一件非常简单的事情,而且在任何地方都没有相关信息。

我有一个返回 Student 的 API 调用,其定义如下

class Student {
  String photo;
  String upn;
  String fullName;
  String studentClass;
  String studentYear;
}

我正在使用 dart http 库来调用返回一组学生的 API。我要做的就是将 API 返回的字符串反序列化为一个类型化的列表,这样我就可以用它做一些事情了。

studentFuture.then((res) {
              log(res.body.toString());
              List<dynamic> dynamicList = jsonDecode(res.body);
              var student = dynamicList[0] as Student;
            });

尝试使用 as 将其投射给 Student 不起作用,我只是明白了

Unhandled Exception: type '_InternalLinkedHashMap&lt;String, dynamic&gt;' is not a subtype of type 'Student' in type cast

我想要的只是能够像在 C# 中那样做这样的事情

var obj = DeserialiseJson<Type>(jsonString); 

无论我尝试什么,我都无法将字符串反序列化为对象,我该怎么做?

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    您无法在 Dart/Flutter 中以您想要的方式执行此操作。这是因为为了在 C#、Java 或任何其他支持此功能的强类型语言中实现此功能,该语言在底层使用反射将 JSON 字段映射到对象字段。

    Dart 在dart:mirror 库中也有反射功能。但是,由于各种类型和字段的所有调试信息以及需要包含在已编译二进制文件中的内容,该库在 Flutter 中被禁用,因为它倾向于创建大型应用程序占用空间。 (这是很简单的解释:Flutter 团队给出的官方给出的理由更详细。)

    这意味着当您反序列化 JSON 字符串时,您会得到一个本质上是 List&lt;dynamic&gt;Map&lt;String, dynamic&gt; 的对象(取决于所讨论的 JSON 字符串;它甚至可能只是一些原始值)。假设后者,您不能直接在 MapStudent 之间进行转换,因为它们是完全不同的类型,没有任何共同之处。没有隐含的方式来获取Map 并生成Student,因为Flutter 应用程序中不存在这样做所需的反射工具。结果,您会收到一条错误消息,提示您无法将 Map 转换为 Student,如您所见。

    要解决此问题,建议您向模型类添加 toJson/fromJson 方法,以便您可以从反序列化的 JSON 字符串显式生成该类的实例:

    class Student {
      String photo;
      String upn;
      String fullName;
      String studentClass;
      String studentYear;
    
      factory Student.fromJson(Map<String, dynamic> jsonData) {
        return Student()
          ..photo = jsonData['photo']
          ..upn = jsonData['upn']
          ..fullName = jsonData['fullName']
          ..studentClass = jsonData['studentClass']
          ..studentYear = jsonData['studentYear'];
      }
    }
    

    然后像这样使用它:

    studentFuture.then((res) {
      log(res.body.toString());
      List<dynamic> dynamicList = jsonDecode(res.body);
      var student = Student.fromJson(dynamicList[0]);
    });
    

    这一切都很好,但是如果您有多个模型类,您很快就会意识到将这个样板代码添加到每个模型类会变得非常乏味。这就是为什么存在各种包来尝试通过自动代码生成来减轻负担的原因,例如json_serializablefreezed

    TL;DR:您所要求的内容目前在 Flutter Dart 中是不可能的,而且这种情况不太可能很快改变。您需要编写手动工厂构造函数来进行转换,或者使用代码生成包为您生成构造函数。

    【讨论】:

    • 这正是我正在寻找的答案,谢谢。使用工厂构造函数而不是常规构造函数有什么要求吗?只是为了清楚吗?
    • @user9754798 简而言之,常规构造函数已经创建了对象,您只需负责实例化字段并执行其他类型的设置。工厂构造函数允许您手动创建要返回到调用构造函数的站点的对象。在您的情况下,这主要取决于选择哪种方式,但是如果您的类有 final 字段(它应该是一个模型类),则您无法使用 Map 中的值实例化类字段像在工厂构造函数中一样的普通构造函数。
    【解决方案2】:

    看看https://pub.dev/packages/json_serializable,它会通过代码生成为您提供类似

    的方法
    @JsonSerializable()
    class Student {
      String photo;
      String upn;
      String fullName;
      String studentClass;
      String studentYear;
      Student({this.photo, this.upn, this.fullName, this.studentClass, this.studentYear});
      factory Student.fromJson(Map<String, dynamic> json) => _$StudentFromJson(json);
      Map<String, dynamic> toJson() => _$StudentToJson(this);
    }
    

    那么你可以这样做:

    var list = jsonDecode(res.body);
    for (var map in list) {
      print(Student.fromJson(map)); // Student class
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多