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

bqucvtff  于 2023-05-04  发布在  Flutter
关注(0)|答案(3)|浏览(177)

当尝试在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); 
}
hgncfbus

hgncfbus1#

因为文档没有充分地涵盖这一点,我花了一天的时间研究Flutter源和不同的东西来解决它的试错。所以不妨分享一下。
Golang默认在RFC 3339中编码time.Time,并将其序列化为Json(如给定示例)。Flutter明确支持RFC 3339,那么为什么它不工作呢?答案是秒小数部分的支持方式有很小的不同。虽然Golang产生7位数的精度,但Dart只支持最多6位数,并且不能优雅地处理违规。因此,如果示例被更正为只有6位精度,它将在Dart中解析得很好:

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

为了以通用的方式解决这个问题,您有两个选择:1.从字符串中截断附加精度,或者2.实现您自己解析。假设我们扩展了DateTime类并创建了自己的CustomDateTime。新类覆盖了parse方法,在将其交给父类的parse方法之前,删除6位数之后的所有多余部分。
现在我们可以在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); 
}

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

14ifxucb

14ifxucb2#

我也有同样的问题。我找到了一个非常简单的解决办法。我们可以通过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);
}
pobjuy32

pobjuy323#

我是这样做的:

import 'package:intl/intl.dart'; //needed for DateFormat

String formatToGolangCompatibleRFC3339NanoUtc(DateTime date) {
    
      return DateFormat("yyyy-MM-ddTHH:mm:ss.SSSSSSSSS+00:00").format(DateTime.parse(date.toString()).toUtc());
}

使用方法:

print("current time: ${formatToGolangCompatibleRFC3339NanoUtc(DateTime.now())}");

输出:

current time: 2023-05-02T14:13:19.275000000+00:00

相关问题