17

我尝试将对象转换为 JSON。

  var obj = { "dt": new DateTime.now() };
  var s = stringify(obj);

运行时抛出异常:“在对象上调用 toJson 方法失败。”

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

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

4

3 回答 3

17

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

日期时间 → JSON

让我们从以下对象开始:

class Person {
  Person(this.name, this.birthdate);
  String name;
  DateTime birthdate;
}

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

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

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

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

解决方案 1

你可以先自己序列化它,如下所示:

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

final jsonString = json.encode(map);

笔记:

toString这是和之间的区别toIso8601String

2020-02-25 14:44:28.534 // toString()
2020-02-25T14:44:28.534 // toIso8601String()

toIso8601String没有任何空格,因此可以更好地进行转换和通过可能无法很好地处理空格的 API 发送。

解决方案 2

您可以toEncodablejsonEncode.

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;
}

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);

请注意,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(),
    };
  }
}

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

笔记

于 2020-02-25T07:34:00.250 回答
3

Zdeslav Vojkovic 的回答现在已经过时了。

中的JSON.encode()方法dart:convert有一个可选toEncodable方法,该方法被调用用于不能原生序列化为 JSON 的对象。然后由用户提供一个返回 DateTime 的适当序列化的闭包。

于 2014-02-16T18:04:47.493 回答
2

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

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

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

所以这里有一些选择:

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

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

于 2013-05-14T09:21:38.073 回答