如何在Dart中将包含DateTime字段的对象转换为JSON?

Ser*_*eyT 8 json dart

我尝试将对象转换为JSON.

  var obj = { "dt": new DateTime.now() };
  var s = stringify(obj);
Run Code Online (Sandbox Code Playgroud)

运行时抛出异常:"在对象上调用toJson方法失败."

这是预期的,因为DateTime类没有toJson方法.但在这种情况下我该怎么办?

Javascript的JSON.stringify函数有一个可选的参数替换器,它允许我提供我自己的任何对象序列化方法,即使该对象没有toJson方法.在Dart中是否有任何类似的工具,或者我可以使用自己的toJson方法以某种方式扩展DateTime类?

Sur*_*gch 14

JSON 转换仅适用于映射、列表、字符串、数字、布尔值或 null。那么,如果您的对象包含另一种类型,例如DateTime?

约会时间 ?JSON

让我们从以下对象开始:

class Person {
  Person(this.name, this.birthdate);
  String name;
  DateTime birthdate;
}
Run Code Online (Sandbox Code Playgroud)

您可以将其转换为这样的地图:

final person = Person('Bob', DateTime(2020, 2, 25));

Map<String, dynamic> map = {
 'name': person.name,
 'birthdate': person.birthdate,
};
Run Code Online (Sandbox Code Playgroud)

如果您现在尝试使用jsonEncode(或json.encode)对其进行编码,则会出现错误,因为DateTime不能直接序列化。有两种解决方案。

解决方案1

你可以先像这样自己序列化它:

Map<String, dynamic> map = {
  'name': person.name,
  'birthdate': person.birthdate.toIso8601String(),
};

final jsonString = json.encode(map);
Run Code Online (Sandbox Code Playgroud)

笔记:

这里的区别toStringtoIso8601String

2020-02-25 14:44:28.534 // toString()
2020-02-25T14:44:28.534 // toIso8601String()
Run Code Online (Sandbox Code Playgroud)

toIso8601String没有任何空间,这样使得它更好的转化率和发送过可能不带空格处理良好的API。

解决方案2

您可以在 上使用可选的toEncodable函数参数jsonEncode

import 'dart:convert';

void main() {
  final person = Person('Bob', DateTime(2020, 2, 25));

  Map<String, dynamic> map = {
    'name': person.name,
    'birthdate': person.birthdate,
  };

  final toJson = json.encode(map, toEncodable: myDateSerializer);
}

dynamic myDateSerializer(dynamic object) {
  if (object is DateTime) {
    return object.toIso8601String();
  }
  return object;
}
Run Code Online (Sandbox Code Playgroud)

toEncodable函数只是将输入转换为字符串或jsonEncode可以转换为字符串的内容。

JSON ? 约会时间

这里没有什么特别的。您只需要将字符串解析为您需要的类型。在这种情况下,DateTime您可以使用它的parsetryParse方法。

final myMap= json.decode(jsonString);
final name = myMap['name'];
final birthdateString = myMap['birthdate'];
final birthdate = DateTime.parse(birthdateString);
final decodedPerson = Person(name, birthdate);
Run Code Online (Sandbox Code Playgroud)

请注意,parse如果字符串的格式无法解析为DateTime对象,则会抛出异常。

作为模型类

class Person {

  Person(this.name, this.birthdate);

  String name;
  DateTime birthdate;

  Person.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        birthdate = DateTime.tryParse(json['birthdate']),

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'birthdate': birthdate.toIso8601String(),
    };
  }
}
Run Code Online (Sandbox Code Playgroud)

如果日期格式错误,这不会引发异常,但birthdate会是null.

笔记


Zde*_*vic 2

dart:jsonIMO,这是库中的一个缺陷,stringify不支持额外的回调来序列化缺乏toJson. 奇怪的是,parse函数确实支持reviver参数来自定义反序列化过程。可能的原因是用户可以toJson为其添加类型,核心库将处理“本机”类型,但DateTime被省略了(可能是因为 JSON 中的日期序列化并不是一个简单的故事)。

也许团队的目标是使用自定义类型而不是 Map(这很好),但这里的缺点是,如果您的自定义类型包含 10 个其他属性和 1 个类型DateTime,则您需要实现toJson将所有这些属性序列化,甚至整数和字符串。希望一旦 Mirror API 准备就绪,就会有库通过读取类型的反射信息来实现“带外”序列化,请参阅下面的示例。Dart 的承诺是我能够在服务器和客户端上使用我的类型,但是如果我必须为我的所有模型手动实现序列化/反序列化,那么它就太尴尬了而无法使用。

它本身在格式化方面不太灵活也无济于事,因为除了返回对序列化无用的格式DateTime之外,没有其他方法。toString

所以这里有一些选择:

  • 在您自己的类型中包装(或派生),DateTime该类型提供toJson
  • 修补json.stringifyJsonValue并提交更改或至少提交一个问题:)
  • 使用一些第三方库json-object(只是一个例子,它也不支持DateTime序列化,AFAIK

我对他们中的任何一个都不满意:)