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

时间:2013-05-14 07:10:09

标签: json dart

我尝试将对象转换为JSON。

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

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

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

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

3 个答案:

答案 0 :(得分:4)

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

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(或json.encode)对此进行编码,则会由于DateTime无法直接序列化而出现错误。有两种解决方案。

解决方案1 ​​

您可以先自己序列化它:

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

final jsonString = json.encode(map);

注意:

这是toStringtoIso8601String之间的区别:

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

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

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

注释

  • 请参阅我的完整答案here
  • 感谢this answer为我指明了正确的方向。

答案 1 :(得分:3)

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

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

答案 2 :(得分:1)

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

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

DateTime本身对格式化不是很灵活也没有用,因为除了toString之外没有其他方法可以返回格式化无用的格式。

所以这里有一些选择:

  • 在您自己的类型中包裹(或派生自)DateTime,提供toJson
  • 修补程序json.stringifyJsonValue并提交更改或至少提交问题:)
  • 使用像json-object这样的第三方派对库(仅作为示例,它也不支持DateTime序列化,AFAIK

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