首页 > 解决方案 > Dart null 安全性和使用 json_serializable 将 JSON 解析为模型

问题描述

在使用各种工具将 JSON 反序列化为 Dart 并且对null 安全性感到非常沮丧之后,我有一个非常普遍的问题。json_serializable生成.fromJson和消息的包.toJson应该关心 NULL 值。但我根本无法做到这一点!所以一百万美元的问题是:

我是否必须将模型的所有属性声明为 NULLABLE(带有 ? 例如String? myString,如果在返回的 JSON 中有可能该属性可能丢失或为 NULL ?

当我不将此类成员声明为 NULLABLE 时,我总是会收到此错误:

Unhandled Exception: type 'Null' is not a subtype of type 'List<dynamic>' in type cast

这些错误总是发生在生成的FromJson方法中。这是导致问题的示例 JSON:

  "responseStatus": {
    "errorCode": "500",
    "message": "This is a test message",
    "errors": [
      {
        "errorCode": "300",
        "fieldName": "a field name",
        "message": "a message",
        "meta": {
          "a key": "a value"
        }
      }
    ],
    "meta": {
      "some field": "some value"
    }
  }

errors数组包含一个数组,该数组本身可能包含也可能不包含( )error objects的数组。如果没有错误则不提交数组,如下所示:key value pairsmetaerrors

"responseStatus": {
    "errorCode": "500",
    "message": "This is a test message",
    "meta": {
      "some field": "some value"
    }

在这种情况下,生成的 DART 代码会崩溃,如下所示:

ResponseStatus _$ResponseStatusFromJson(Map<String, dynamic> json) =>
    ResponseStatus(
      errorCode: json['errorCode'] as String? ?? '',
      message: json['message'] as String? ?? '',
      stackTrace: json['stackTrace'] as String? ?? '',
    )
      ..errors = (json['errors'] as List<dynamic>)  // <<==== This is crashing if errors is not in the returned JSON
          .map((e) => ResponseError.fromJson(e as Map<String, dynamic>))
          .toList()
      ..meta = Map<String, String>.from(json['meta'] as Map);

我创建的模型类如下所示:

@JsonSerializable(explicitToJson: true)
class ResponseStatus extends Equatable {
  late final String errorCode;
  late final String message;
  late final String stackTrace;
  late final List<ResponseError> errors;
  late final Map<String, String> meta;


  ResponseStatus({this.errorCode = '', this.message = '', this.stackTrace = ''}) {
    this.errors = [];
    this.meta = Map();
  }

  factory ResponseStatus.fromJson(Map<String, dynamic> json) =>
      _$ResponseStatusFromJson(json);

  Map<String, dynamic> toJson() => _$ResponseStatusToJson(this);

  @override
  List<Object?> get props => [errorCode, message, stackTrace, errors, meta];

}

我创建了一个默认构造函数,它用一个值初始化所有成员(不可为空)。

如果我必须声明所有模型成员,因为nullable这将无法使用,因为在我的颤振小部件中,我必须编写数千个if statements来检查成员是否为空。我认为避免这种情况是 sound null safety 背后的想法之一

我很想知道这是否是一个错误或者我是如何让这个东西工作的!

标签: dartjson-serializable

解决方案


这是预期的行为。如果未给出数据,则必须发生崩溃,因为不可为空的变量永远不会null值。

但是,您可以使用自定义反序列化函数来处理这种情况:

@JsonKey(fromJson: optionalErrorListFromJson)
late final List<ResponseError> errors;
List<ResponseError> optionalErrorListFromJson(List<dynamic>? errors) {
  if (errors == null) return const [];
  return errors
      .map((e) => ResponseError.fromJson(e as Map<String, dynamic>))
      .toList(growable: false)
}

推荐阅读