Flutter JSON 序列化 - 不生成 *.g.dart 文件

Posted

技术标签:

【中文标题】Flutter JSON 序列化 - 不生成 *.g.dart 文件【英文标题】:Flutter JSON Serialization - Not generating *.g.dart files 【发布时间】:2020-02-10 02:47:10 【问题描述】:

我是 Flutter 的新手,目标是序列化包含其他较小对象的复杂 JSON 对象。

使用json_serializable: ^2.0.0pubspec.yaml 文件看起来像这样。

dependencies:
  intl: ^0.15.7
  json_annotation: ^2.0.0
  built_value: ^6.7.1
  flutter:
    sdk: flutter

dev_dependencies:
  build_runner: ^1.0.0
  json_serializable: ^2.0.0
  built_value_generator: ^6.7.1
  flutter_test:
    sdk: flutter

user.dart 看起来像这样

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable(nullable: false)
class User 
  final String firstName;
  final String lastName;
  final DateTime dateOfBirth;
  User(this.firstName, this.lastName, this.dateOfBirth);
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);

我已经尝试过flutter pub run build_runner build,但文件 user.g.dart 没有被创建,我遇到了这个问题。

我还添加了build.yaml 文件,代码如下

targets:
  $default:
    builders:
      built_value_generator|built_value:
        generate_for:
          - model/*.dart
      json_serializable|json_serializable:
        generate_for:
          - model/*.dart

谁能让我知道我在这里缺少什么。谢谢

【问题讨论】:

【参考方案1】:

清单

您的类文件在 /lib 或 /bin 下 可以是那些下的子目录 json_serializable 不会在每个目录中搜索要生成的文件。 为 json_annotation 添加了导入: import 'package:json_annotation/json_annotation.dart'; 您在import 语句之后添加了一个part 指令 您的part 文件以您的类文件名(不是类名本身)命名,并添加了g 例如对于CacheItem 类... cache-item.dart 类文件名 ... part 'cache-item.g.dart'; 获取对应的part 指令。 part 指令不是以您的实际类命名的,而是类文件名。 您已在类名上方添加@JsonSerializable() 您已经为您的类创建了一个默认构造函数。 它可以是空的、有可选的命名参数或位置参数。 只要您的类字段可访问(通过构造函数或公共 setter 和 getter),json_serializable 就可以处理它。 (即不只有 _private 属性和一个空的构造函数) 您编写了两个调用私有存根方法的方法: 工厂fromJson方法 例如:factory CacheItem.fromJson(Map&lt;String,dynamic&gt; json) =&gt; _CacheItemFromJson(json) toJson 方法 例如:Map&lt;String,dynamic&gt; toJson() =&gt; _$CacheItemToJson(this) 存根方法是私有的(以_ 下划线开头) $tub 方法有$ 存根方法具有适当的 CaSe(即 Pascal Case) 存根 factory 提供 (Map&lt;String,dynamic&gt; json) 作为参数 存根toJson()返回Map&lt;String,dynamic&gt;

所有这些都完成后,尝试从项目根目录中的命令行或终端运行生成器...

在颤振中:

flutter pub run build_runner build

在纯 Dart 中,这取决于您的版本,但其中之一应该可以工作:

dart run build_runner build
pub run build_runner build
dart pub run build_runner build

如果一切顺利,在您的项目文件资源管理器或Reload from disk 中单击,新文件应该会出现,例如上述示例中的cache-item.g.dart

常见错误

Bad state: Unexpected diagnostics:

在运行 build_runner 时看到此输出可能是颤振和json_annotation 的问题,具体取决于analyzer 的不兼容版本。这发生在json_serializable 版本3.5 之前 需要dependency_override of analyzer 到 0.39.14 或 0.39.17。

您的第一步应该是尝试最新版本的json_serilizable from pub.dev apparently doesn't have this dependency problem。

如果您无法升级 json_serializable,您可以尝试将覆盖线放在 dev_dependences 下方:

dev_dependencies:
  build_runner: ^1.9.0
  flutter_test:
    sdk: flutter
  json_serializable: 3.3.0
  test: ^1.14.3

dependency_overrides:
  analyzer: '0.39.14'

[SEVERE] Nothing can be built, yet a build was requested.

当我们在 pubspec.yaml 中为 json_annotation 添加依赖项但缺少 json_serializable 的依赖项/dev_dependency 时,执行 flutter pub run build_runner build 时可能会发生此错误:

dependencies:
  flutter:
    sdk: flutter
  get:
  json_annotation: ^4.3.0
  some_other_packages:

确保您已将 json_serializable 包添加为依赖项或 dev_dependency:

dependencies:
  flutter:
    sdk: flutter
  get:
  json_annotation: ^4.3.0

dev_dependencies:
  build_runner: ^2.1.4
  flutter_test:
    sdk: flutter
  json_serializable: ^6.0.1  #// ← do not forget
  test:

Could not generate fromJsoncode forsomeField.

如果您要对包含 someField 的类进行 json 序列化,该类是您创建的另一个自定义类的类型,您有 @JsonSerializable() 那个其他自定义类吗?

@JsonSerializable(explicitToJson: true)
class BuildingStatus 
  final Building building; // another custom class

  BuildingStatus(Building building);

  factory BuildingStatus.fromJson(Map<String,dynamic> json) => _$BuildingStatusFromJson(json);
  Map<String,dynamic> toJson() => _$BuildingStatusToJson(this);



/// This guy needs serialization too.
@JsonSerializable()
class Building 
  final String name;

  const Building(this.name);

  factory Building.fromJson(Map<String,dynamic> json) => _$BuildingFromJson(json);
  Map<String,dynamic> toJson() => _$BuildingToJson(this);


如果不序列化嵌套的 Building 类,我们会在运行 build_runner 时看到如下错误:

Could not generate `fromJson` code for `building` because of type `Building`.

“SomeNestedClass”的实例

如果我们有嵌套的可序列化类,我们通常希望序列化以递归方式发生。即嵌套类也被序列化。

为此,我们将使用 explicitToJson: true 注释我们的包含类,例如: @JsonSerializable(explicitToJson: true)

所以当我们toJson() 我们的BuildingStatus 实例时,而不是得到:

"building": Instance of 'Building'

...我们会得到:

"building": "name": "Empire State"

注意事项

子类/父类

如果您的类是父类的子类,并且您只想序列化子类的字段/属性,则可以仅注释子类。父类字段将自动找到并包含在为子类生成的类文件中。

如果您希望能够同时分别序列化/反序列化父类和子类,请继续使用@JsonSerializable 注释基类/父类。

例如文件名account.dart

import 'package:json_annotation/json_annotation.dart';

part 'account.g.dart';

class AccountBase 
  int created;
  String username;
  String password;


@JsonSerializable()
class Account extends AccountBase 
  int id;

  Account();

  factory Account.fromJson(Map<String,dynamic> json) => _$AccountFromJson(json);
  Map<String,dynamic> toJson() => _$AccountToJson(this);

生产:

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'account.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Account _$AccountFromJson(Map<String, dynamic> json) 
  return Account()
    ..created = json['created'] as int
    ..username = json['username'] as String
    ..password = json['password'] as String
    ..id = json['id'] as int;


Map<String, dynamic> _$AccountToJson(Account instance) => <String, dynamic>
      'created': instance.created,
      'username': instance.username,
      'password': instance.password,
      'id': instance.id,
    ;

参考和文档

示例project on github,/bin/下的相关文件和pubspec.yaml中的包 Flutter & Json Json_Serializable package Example 来自软件包作者

示例

import 'package:json_annotation/json_annotation.dart';

part 'cache-item.g.dart';

@JsonSerializable()
class CacheItem 
  int created;
  String keywords;
  String response;

  CacheItem(this.created, this.keywords, this.response);

  factory CacheItem.fromJson(Map<String,dynamic> json) => _$CacheItemFromJson(json);
  Map<String,dynamic> toJson() => _$CacheItemToJson(this);

输出

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'cache-item.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

CacheItem _$CacheItemFromJson(Map<String, dynamic> json) 
  return CacheItem(
    json['created'] as int,
    json['keywords'] as String,
    json['response'] as String,
  );


Map<String, dynamic> _$CacheItemToJson(CacheItem instance) => <String, dynamic>
      'created': instance.created,
      'keywords': instance.keywords,
      'response': instance.response,
    ;


示例构造函数变体

除了构造函数缺少一些字段并且response 是可选的之外,此示例与上面的示例相同。

没关系。

生成器将在实例化对象后仅使用公共(隐式)设置器来分配值。

import 'package:json_annotation/json_annotation.dart';

part 'cache-item.g.dart';

@JsonSerializable()
class CacheItem 
  int created;
  String keywords;
  String response;

  CacheItem(this.response);

  factory CacheItem.fromJson(Map<String,dynamic> json) => _$CacheItemFromJson(json);
  Map<String,dynamic> toJson() => _$CacheItemToJson(this);

输出

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'cache-item.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

CacheItem _$CacheItemFromJson(Map<String, dynamic> json) 
  return CacheItem(
    response: json['response'] as String,
  )
    ..created = json['created'] as int
    ..keywords = json['keywords'] as String;


Map<String, dynamic> _$CacheItemToJson(CacheItem instance) => <String, dynamic>
      'created': instance.created,
      'keywords': instance.keywords,
      'response': instance.response,
    ;

【讨论】:

这个真的很有帮助,最重要的是不要使用额外的依赖项,只需使用@Baker 建议的内容。 +1 我已经检查了一切,并确认不生成那些模型-json转换方法 有史以来最好的答案! 就我而言,它是关于 .g.dart 文件名;我试图输入“xx_quentity_model.g.dart”,但我的 dart 文件名是“xx_quantity_model.dart”所以,我的 .g.dart 文件没有生成,所以如果你在检查列表中没有发现任何错误,也许你可以控制.g.dart 文件名(用于类型错误)。【参考方案2】:

构造函数的参数不应该是可选的

User(this.firstName, this.lastName, this.dateOfBirth);

它们应该是强制性的:

User(this.firstName, this.lastName, this.dateOfBirth);

还有部分

'user.g.dart';

应该匹配大写用户类:

part 'User.g.dart';

【讨论】:

我试过使用可选参数,效果很好。快乐飘飘。 强制参数是正确的,但大写字母并没有真正起作用。但是争论。 谢谢,可选参数构造函数对我来说似乎是个问题。 如果你在一个名为logged_user.dart的文件中有一个类似LoggedUser的类,那么使用part 'logged_user.g.dart' 如果您在项目中使用包,首先需要转到该包路径并运行“flutter pub run build_runner build”【参考方案3】:

尝试运行

>flutter packages pub run build_runner build --delete-conflicting-outputs

【讨论】:

【参考方案4】:

文件名、类和part 'Book.g.dart'; 应该都匹配。

【讨论】:

您对 Jalil 回答的评论表明您可以使用可选参数构造函数。您有机会将代码放入此答案吗?因为从选项更改为必需的参数是唯一让这对我有用的事情。【参考方案5】:

我遇到了以下错误

错误

[WARNING] json_serializable:json_serializable on lib/day10/models/sentence.dart:
Missing "part 'sentence.g.dart';".

我注意到我必须将模型文件中的 part 'Sentence.g.dart'; 更改为 part 'sentence.g.dart';,换句话说,我必须将其小写。

【讨论】:

【参考方案6】:

我知道,在运行命令之前必须保存文件flutter packages pub run build_runner build

我知道答案似乎很简单。

【讨论】:

我进行了一些编辑,以减少您描述自己的问题的印象(这里的用户很容易混淆)。我注意到有问题的文件没有明确标识。您可能想要更明确。否则感谢您的贡献。 刚刚安装 vs code 并尝试过的情况对我来说完全一样 :( 感谢您的提示【参考方案7】:

就我而言,我必须添加 hive_generator 包以及 build_runner 包。 如果您的模型类名称是例如 transaction.dart,那么它将是这样的 - part 'transaction.g.dart' 运行命令—— flutter packages pub run build_runner build它会为你生成交易模型。

【讨论】:

【参考方案8】:

就我而言,问题在于我拼错了方法名称。我写的是 formJson 而不是 fromJson。

factory MyClass.formJson(Map<String, dynamic> json) =>
_$MyClassFromJson(json);

应该是:

factory MyClass.fromJson(Map<String, dynamic> json) =>
_$MyClassFromJson(json);

【讨论】:

【参考方案9】:

添加

json_serializable: ^6.0.0

到 dev_dependencies 为我解决它

【讨论】:

以上是关于Flutter JSON 序列化 - 不生成 *.g.dart 文件的主要内容,如果未能解决你的问题,请参考以下文章

Flutter -- 进阶JSON 数据

[译]Flutter JSON和序列化

Flutter实现网络请求

Flutter -------- 解析JSON数据

在flutter中使用json_serializable对json对象进行反序列化

Flutter - 提取到 FutureBuilder 的 JSON 反序列化数据返回空值