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

时间:2018-10-13 03:20:02

标签: json go dart flutter

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

FormatException: Invalid date format

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

{
    ...
    "dateCreated": "2018-09-29T19:51:57.4139787-07:00",
    ...
}

我正在使用代码生成方法进行json(反序列化),以避免编写所有样板代码。 json_serializable软件包是可用于此目的的标准软件包。所以我的代码如下所示:

@JsonSerializable()
class MyObj {

  DateTime dateCreated;

  MyObj( this.dateCreated);

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

2 个答案:

答案 0 :(得分:4)

由于文档不够全面,因此我花了一天的时间研究颤动源以及各种问题的反复试验来解决。因此不妨分享一下。

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

{
    ...
    "dateCreated": "2018-09-29T19:51:57.413978-07:00",
    ...
}

为了以一种通用的方式解决这个问题,您有两个选择:1.从字符串中截断附加精度,或者2.实现自己的解析。假设我们扩展DateTime类并创建自己的CustomDateTime。新类具有parse方法,该方法被覆盖以除去6位数字之后的所有多余字符,然后将其交给父类的parse方法。

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

@JsonSerializable()
class MyObj {

  CustomDateTime dateCreated;

  MyObj( this.dateCreated);

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

但是,当然,现在代码生成已中断,并且出现以下错误:

Error running JsonSerializableGenerator
Could not generate 'toJson' code for 'dateCreated'.
None of the provided 'TypeHelper' instances support the defined type.

幸运的是,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();
}

第二,我们只是将此转换器注释为使用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); 
}

这满足了代码生成器和Voila!我们可以使用来自golang time.Time的RFC3339时间戳读取json。

答案 1 :(得分:1)

我遇到了同样的问题。我找到了一个非常简单的解决方案。我们可以在JsonConverter中使用自定义转换器。有关更多说明,请使用我的article

import 'package:json_annotation/json_annotation.dart';

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

  @override
  DateTime fromJson(String json) {
    if (json.contains(".")) {
      json = json.substring(0, json.length - 1);
    }

    return DateTime.parse(json);
  }

  @override
  String toJson(DateTime json) => json.toIso8601String();
}

import 'package:json_annotation/json_annotation.dart';
import 'package:my_app/shared/helpers/custom_datetime.dart';

part 'publication_document.g.dart';

@JsonSerializable()
@CustomDateTimeConverter()
class PublicationDocument {
  final int id;
  final int publicationId;

  final DateTime publicationDate;
  final DateTime createTime;
  final DateTime updateTime;
  final bool isFree;

  PublicationDocument({
    this.id,
    this.publicationId,
    this.publicationDate,
    this.createTime,
    this.updateTime,
    this.isFree,
  });

  factory PublicationDocument.fromJson(Map<String, dynamic> json) =>
      _$PublicationDocumentFromJson(json);
  Map<String, dynamic> toJson() => _$PublicationDocumentToJson(this);
}