如何在Flutter中将对象编码为JSON

44

我正在尝试将对象"Week"转换为JSON。

https://flutter.dev/docs/development/data-and-backend/json 这是我使用的来源。

class Week{
  DateTime _startDate;
  DateTime _endDate;
  List<Goal> _goalList;
  String _improvement;

  Week(this._startDate, this._endDate){
    this._goalList = List<Goal>();
    this._improvement = "";
  }

  Week.fromJson(Map<String, dynamic> json)
    : _startDate =  json['startDate'],
      _endDate = json['endDate'],
      _goalList = json['goalList'],
      _improvement = json['improvement'];

  Map<String, dynamic> toJson() => 
  {
    'startDate': _startDate,
    'endDate': _endDate,
    'goalList': _goalList,
    'improvement': _improvement,
  };
}

我使用了这个:

DateTime startDate = currentDate.subtract(new Duration(days:(weekday-1)));
DateTime endDate = currentDate.add(new Duration(days:(7-weekday)));

Week week = new Week(startDate, endDate);
var json = jsonEncode(week);

但问题在于我得到了这个结果:

Unhandled Exception: Converting object to an encodable object failed: Instance of 'Week'
#0      _JsonStringifier.writeObject (dart:convert/json.dart:647:7)
#1      _JsonStringStringifier.printOn (dart:convert/json.dart:834:17)
#2      _JsonStringStringifier.stringify (dart:convert/json.dart:819:5)
#3      JsonEncoder.convert (dart:convert/json.dart:255:30)
#4      JsonCodec.encode (dart:convert/json.dart:166:45)
#5      jsonEncode (dart:convert/json.dart:80:10)

就我个人而言,我认为将方法命名为“toJson”有些不妥,因为它们实际上是“toMapStringDynamic”方法。真正执行“toJson-ing”的是jsonEncode函数。 - Chris Nadovich
3个回答

56

jsonEncode需要一个Map<String, dynamic>,而不是一个Week对象。调用你的toJson()方法即可解决问题。

var json = jsonEncode(week.toJson());

请注意,你的toJson()方法也是错误的,因为_goalList和日期等内容仍然是对象而不是Maps或Lists。你还需要在这些对象上实现toJson方法。

回答你具体的问题:

  1. Dart不是JavaScript/TypeScript。Dart在运行时检查类型,因此你必须明确告诉它如何转换数据类型。此外,在Dart中没有反射,所以它无法自动处理。
  2. 你可以使用一个利用代码生成来自动完成这些工作的库 - 但在运行时仍然无法完成 - 更多信息可以参考JSON serialization
  3. 最简单的方法是直接在类中实现这些方法,因为这是你根对象中拥有访问权限的地方。请记住,jsonEncode所需的结构是Map<String,dynamic>,但是dynamic部分实际上意味着List<dynamic>Map<String,dynamic>或与JSON兼容的基元类型,例如Stringdouble - 如果你试图想象此类类型的嵌套结构的样子,你会意识到它基本上就是JSON。因此,当你执行像'goalList': _goalList,这样的代码时,实际上是在给它一个对象,而不是允许的类型之一。

希望这能稍微解释清楚一些问题。


谢谢您的快速回复。我现在有几个问题:1.为什么将对象转换为JSON这么复杂?2.没有更简单的方法吗?3.我该如何实现那些toJson()方法?我必须制作单独的类还是可以将它们实现到Week类中? - laaasBIGL
对象内部的对象怎么办? - Umesh Chakradhar
在Flutter/Dart中,对于复杂嵌套对象中的常规数据字段进行序列化似乎需要大量的样板工作。肯定有更好的方法吧?!渴望语言支持的魔法,可以像PHP中那样从对象转换为映射,再从映射转回对象。 - Chris Nadovich

23

如果有人想知道:我已经找到了解决方法。

为了让我的代码工作,我需要在我的类Goal中实现toJson()方法(因为我在Week中使用了List<Goal>)。

class Goal{
  String _text;
  bool _reached;

  Map<String, dynamic> toJson() =>
  {
    'text': _text,
    'reached': _reached,
  }; 
}

还有,我需要将DateTime对象添加 .toIso8601String() ,就像在Week类中那样:

Also, 我需要将DateTime对象添加 .toIso8601String() ,就像在Week类中那样:

Map<String, dynamic> toJson() => 
  {
    'startDate': _startDate.toIso8601String(),
    'endDate': _endDate.toIso8601String(),
    'goalList': _goalList,
    'improvement': _improvement,
  };

现在的输出结果是:

 {"startDate":"2019-05-27T00:00:00.000Z","endDate":"2019-06-02T00:00:00.000Z","goalList":[],"improvement":""}

1
非常感谢@Philip的出色解释! - laaasBIGL
我有一个问题。你是如何编写这个星期的构造函数的?Week(this._startDate, this._endDate){ this._goalList = List<Goal>(); this._improvement = ""; }你如何实例化这样的类? - Bilaal Abdel Hassan
我的意思是你是如何给_goalList和_improvement赋值的? - Bilaal Abdel Hassan

0
从@Phillip的答案中采用建议2进行Json序列化,我相信您可以跳过@JsonSerializable注释,只使用@Freezed注释,因为Freezed“将自动要求json_serializable生成所有必需的fromJson/toJson。” 因此,这里的示例:https://flutter.dev/docs/development/data-and-backend/json#use-code-generation-for-medium-to-large-projects 变成了:
//import 'package:json_annotation/json_annotation.dart';
import 'freezed_annotation/freezed_annotation.dart';

/// This allows the `User` class to access private members in
/// the generated file. The value for this is *.g.dart, where
/// the star denotes the source file name.
part 'user.g.dart';
part 'user.freezed.dart';
/// An annotation for the code generator to know that this class needs the
/// JSON serialization logic to be generated.
@freezed
class User {
  User(this.name, this.email);

  String name;
  String email;

  /// A necessary factory constructor for creating a new User instance
  /// from a map. Pass the map to the generated `_$UserFromJson()` constructor.
  /// The constructor is named after the source class, in this case, User.
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  /// `toJson` is the convention for a class to declare support for serialization
  /// to JSON. The implementation simply calls the private, generated
  /// helper method `_$UserToJson`.
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

冻结: https://pub.dev/packages/freezed 别忘了编辑pubspec.yaml文件以使用freezedfreezed_annotation


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接