Flutter 使用 Golang RFC3339 中的 DateTime 解析 json:FormatException:无效的日期格式

Osw*_*ann 3 json go dart flutter

当尝试在 Dart / Flutter 中读取使用 golangs json 包生成的 json 文件时,我注意到解析日期会产生错误:

FormatException: Invalid date format
Run Code Online (Sandbox Code Playgroud)

一个示例是在 Go 服务器上生成的以下 json:

{
    ...
    "dateCreated": "2018-09-29T19:51:57.4139787-07:00",
    ...
}
Run Code Online (Sandbox Code Playgroud)

我使用 json(反)序列化的代码生成方法来避免编写所有样板代码。json_serialized 包是可用于此目的的标准包。所以我的代码如下所示:

@JsonSerializable()
class MyObj {

  DateTime dateCreated;

  MyObj( this.dateCreated);

  factory MyObj.fromJson(Map<String, dynamic> json) => _$MyObjFromJson(json);  
  Map<String, dynamic> toJson() => _$MyObjToJson(this); 
}
Run Code Online (Sandbox Code Playgroud)

Osw*_*ann 7

因为文档没有充分涵盖这个问题,所以我花了一天的时间研究颤振源并尝试和错误不同的事情来解决它。所以不妨分享一下。

当序列化为 Json 时,Golang 默认对 RFC3339 中的 time.Time 进行编码(如给定的示例)。Flutter明确支持RFC3339,那么为什么它不起作用呢?答案是秒小数部分的支持方式略有不同。虽然 Golang 产生 7 位数字的精度,但 Dart 最多只支持 6 位数字,并且不能优雅地处理违规。因此,如果示例被更正为只有 6 位精度,它将在 Dart 中正常解析:

{
    ...
    "dateCreated": "2018-09-29T19:51:57.413978-07:00",
    ...
}
Run Code Online (Sandbox Code Playgroud)

为了以通用方式解决此问题,您有两种选择:1. 截断字符串中的额外精度,或 2. 实现您自己的解析。假设我们扩展该类DateTime并创建您自己的CustomDateTime. 新类重写了该parse方法,以在将其传递给父类的解析方法之前删除 6 位数字后的所有多余内容。

CustomDateTime现在我们可以在 Dart 类中使用。例如:

@JsonSerializable()
class MyObj {

  CustomDateTime dateCreated;

  MyObj( this.dateCreated);

  factory MyObj.fromJson(Map<String, dynamic> json) => _$MyObjFromJson(json);  
  Map<String, dynamic> toJson() => _$MyObjToJson(this); 
}
Run Code Online (Sandbox Code Playgroud)

但当然现在代码生成被破坏了,我们收到以下错误:

Error running JsonSerializableGenerator
Could not generate 'toJson' code for 'dateCreated'.
None of the provided 'TypeHelper' instances support the defined type.
Run Code Online (Sandbox Code Playgroud)

幸运的是,该json_annotation软件包现在为我们提供了一个简单的解决方案 - JsonConverter. 以下是在我们的示例中使用它的方法:

首先定义一个转换器,向代码生成器解释如何转换我们的CustomDateTime类型:

class CustomDateTimeConverter implements JsonConverter<CustomDateTime, String> {
  const CustomDateTimeConverter();

  @override
  CustomDateTime fromJson(String json) =>
      json == null ? null : CustomDateTime.parse(json);

  @override
  String toJson(CustomDateTime object) => object.toIso8601String();
}
Run Code Online (Sandbox Code Playgroud)

其次,我们只需将此转换器注释到使用 CustomDateTime 数据类型的每个类:

@JsonSerializable()
@CustomDateTimeConverter()
class MyObj {

  CustomDateTime dateCreated;

  MyObj( this.dateCreated);

  factory MyObj.fromJson(Map<String, dynamic> json) => _$MyObjFromJson(json);  
  Map<String, dynamic> toJson() => _$MyObjToJson(this); 
}
Run Code Online (Sandbox Code Playgroud)

这满足了代码生成器的要求,瞧!我们可以读取带有来自 golang time.Time 的 RFC3339 时间戳的 json。