[译]Flutter JSON和序列化

 

很难想象一个移动应用程序不需要与Web服务器通信或在某些时候容易存储结构化数据。制作网络连接的应用程序时,迟早需要消耗一些好的旧JSON。

本指南介绍了如何在Flutter中使用JSON。它涵盖了在不同场景中使用哪种JSON解决方案,以及原因。

哪种JSON序列化方法适合我?

本文介绍了使用JSON的两种常规策略:

  • 手动序列化
  • 使用代码生成进行自动序列化

不同的项目具有不同的复杂性和用例。对于较小的概念验证项目或快速原型,使用代码生成器可能过度。对于具有更多复杂性的多个JSON模型的应用程序,手动编码很快就会变得乏味,重复,并且适用于许多小错误。

对较小的项目使用手动序列化

手动JSON解码是指使用内置的JSON解码器 dart:convert。它涉及将原始JSON字符串传递给json.decode() 方法,然后Map<String, dynamic> 在方法返回时查找所需的值。它没有外部依赖性或特定的设置过程,它有利于快速验证概念。

当项目变大时,手动解码效果不佳。手动编写解码逻辑可能变得难以管理且容易出错。如果在访问不存在的JSON字段时出现拼写错误,则代码会在运行时抛出错误。

如果您的项目中没有很多JSON模型,并且希望快速测试概念,那么手动序列化可能就是您想要的方式。有关手动编码的示例,请参阅 使用dart:convert手动序列化JSON

使用代码生成中大型项目

使用代码生成的JSON序列化意味着使用外部库为您生成编码样板。进行一些初始设置后,您将运行一个文件监视器,从您的模型类生成代码。例如, json_serializable和 built_value 就是这些类型的库。

这种方法适用于较大的项目。不需要手写的样板文件,并且在编译时捕获访问JSON字段时的拼写错误。代码生成的缺点是它需要一些初始设置。此外,生成的源文件可能会在项目导航器中产生视觉混乱。

当您拥有中型或大型项目时,您可能希望使用生成的代码进行JSON序列化。要查看基于JSON编码的代码生成示例,请参阅 使用代码生成库序列化JSON

Flutter中是否有GSON / Jackson / Moshi等价物?

简单回答是不。

这样的库需要使用运行时反射,这在Flutter中被禁用。运行时反射会干扰树抖动,Dart已经支持了很长时间。在树摇动的情况下,您可以从发布版本中“摆脱”未使用的代码。这显着优化了应用程序的大小。

由于反射使得默认情况下隐式使用所有代码,因此使树难以振动。这些工具无法知道运行时哪些部分未使用,因此冗余代码很难剥离。使用反射时,应用程序大小无法轻松优化。

** dartson怎么样?**

dartson库使用运行时反射,这使得它不兼容flutter。

虽然您不能在Flutter中使用运行时反射,但是某些库为您提供了类似的易用API,而是基于代码生成。代码生成库部分更详细地介绍了此方法。

使用dart:convert手动序列化JSON

Flutter中的基本JSON编码非常简单。Flutter有一个内置 dart:convert库,包括一个简单的JSON编码器和解码器。

以下是简单用户模型的示例JSON。

{
"name": "John Smith",
"email": "john@example.com"
}

有了dart:convert,您可以通过两种方式对此JSON模型进行编码。

序列化JSON内联

通过查看dart:转换JSON文档,您将看到可以通过调用json.decode方法解码JSON ,并使用JSON字符串作为方法参数。

Map<String, dynamic> user = json.decode(json);

print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');

不幸的是,json.decode()只返回a Map<String, dynamic>,这意味着在运行时之前您不知道值的类型。使用这种方法,您将丢失大多数静态类型语言功能:类型安全性,自动完成以及最重要的编译时异常。您的代码将立即变得更容易出错。

例如,无论何时访问nameemail字段,都可能会快速引入拼写错误。由于JSON存在于地图结构中,编译器不知道的拼写错误。

在模型类中序列化JSON

通过引入User在此示例中调用的普通模型类来对抗前面提到的问题。在User课堂上,你会发现:

  • 一个User.fromJson构造函数,构造一个新的User从地图结构实例。
  • 一种toJsonUser实例转换为地图的方法。

使用这种方法,调用代码可以具有类型安全性,nameemail字段的自动完成以及编译时异常。如果您使用拼写错误或将字段视为ints而不是Strings,则应用程序将无法编译,而不是在运行时崩溃。

user.dart

class User {
final String name;
final String email; User(this.name, this.email); User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email']; Map<String, dynamic> toJson() =>
{
'name': name,
'email': email,
};
}

解码逻辑的责任现在在模型本身内部移动。使用这种新方法,您可以轻松解码用户。

Map userMap = json.decode(json);
var user = new User.fromJson(userMap); print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');

要对用户进行编码,请将User对象传递给json.encode方法。您不需要调用该toJson方法,因为json.encode 已经为您完成了。

String json = json.encode(user);

使用这种方法,调用代码根本不必担心JSON序列化。但是,模型类仍然必须。在生产应用程序中,您需要确保序列化正常工作。在实践中,这些User.fromJsonUser.toJson 方法都需要进行单元测试以验证正确的行为。

但是,现实场景通常不那么简单。您不太可能使用如此小的JSON响应。嵌套的JSON对象也是常用的。

如果有一些东西可以为您处理JSON编码和解码,那就太好了。幸运的是,有!

使用代码生成库序列化JSON

虽然还有其他库可用,但本指南使用 json_serializable包,这是一个自动生成的源代码生成器,可为您生成JSON序列化样板。

由于序列化代码不再是手动或手动维护的,因此可以最大限度地降低在运行时出现JSON序列化异常的风险。

在项目中设置json_serializable

要包含json_serializable在项目中,您需要一个常规依赖项和两个dev依赖项。简而言之,dev依赖项 是我们的应用程序源代码中未包含的依赖项 - 它们仅在开发环境中使用。

可以通过遵循 JSON可序列化示例中的pubspec文件来查看这些必需依赖项的最新版本 。

pubspec.yaml

dependencies:
# Your other regular dependencies here
json_annotation: ^2.0.0 dev_dependencies:
# Your other dev_dependencies here
build_runner: ^1.0.0
json_serializable: ^2.0.0

flutter packages get在项目根文件夹中运行(或单击 编辑器中的Packages Get)以在项目中使用这些新的依赖项。

以json_serializable方式创建模型类
以下显示如何将User类转换为一个类json_serializable 。为简单起见,此代码使用先前示例中的简化JSON模型。

user.dart

class User {
final String name;
final String email; User(this.name, this.email); User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email']; Map<String, dynamic> toJson() =>
{
'name': name,
'email': email,
};
}

采用这种设置,源代码生成器用于编码和将编码生成代码nameemail从JSON字段。

如果需要,还可以轻松自定义命名策略。例如,如果API返回带有snake_case的对象,并且您想在模型中使用 lowerCamelCase,则可以使用@JsonKey带有name参数的注释:

/// Tell json_serializable that "registration_date_millis" should be
/// mapped to this property.
@JsonKey(name: 'registration_date_millis')
final int registrationDateMillis;

运行代码生成实用程序

json_serializable第一次创建类时,您将收到类似于下图所示的错误。

当模型类的生成代码尚不存在时,IDE警告。

这些错误完全正常,仅仅是因为模型类的生成代码尚不存在。要解决此问题,请运行生成序列化样板的代码生成器。

有两种运行代码生成器的方法。

一次性代码生成

通过flutter packages pub run build_runner build在项目根目录中运行,可以在需要时为模型生成JSON序列化代码。这会触发一次性构建,该构建遍历源文件,选择相关文件,并为它们生成必要的序列化代码。

虽然这很方便,但如果您不必每次在模型类中进行更改时都必须手动运行构建,那将是很好的。

不断生成代码

一个观察者,使我们的源代码生成的过程更加方便。它会监视项目文件中的更改,并在需要时自动构建必要的文件。通过flutter packages pub run build_runner watch在项目根目录中运行来启动观察程序 。

启动观察者一次并让它在后台运行是安全的。

使用json_serializable模型

要以这种json_serializable方式解码JSON字符串,您实际上没有对我们以前的代码进行任何更改。

Map userMap = json.decode(json);
var user = User.fromJson(userMap);

编码也是如此。调用API与以前相同。

String json = json.encode(user);

有了json_serializable,您可以忘记User该类中的任何手动JSON序列化 。源代码生成器创建一个名为的文件user.g.dart,该文件具有所有必需的序列化逻辑。您不再需要编写自动化测试来确保序列化工作 - 现在图书馆有责任确保序列化正常工作。

进一步参考

有关更多信息,请参阅以下资源:

[译]Flutter JSON和序列化的更多相关文章

  1. 05-06 Flutter JSON和序列化反序列化、创建模型类转换Json数据、轮播图数据渲染:Flutter创建商品数据模型 、请求Api接口渲染热门商品 推荐商品

    Config.dart class Config{ static String domain='http://jd.itying.com/'; } FocusModel.dart class Focu ...

  2. flutter json_annotation和json_serializable处理json数据序列化

    flutter json_annotation和json_serializable处理json数据序列化 导包 dependencies: json_annotation: ^2.4.0 dev_de ...

  3. Flutter json 2 model with Built Value

    Flutter json 2 model with Built Value Flutter中json转换model, 除了手动转之外, 就是利用第三方库做一些代码生成. 流行的库有: json_ser ...

  4. [.net 面向对象程序设计进阶] (12) 序列化(Serialization)(四) 快速掌握JSON的序列化和反序列化

    [.net 面向对象程序设计进阶] (12) 序列化(Serialization)(四) 快速掌握JSON的序列化和反序列化 本节导读: 介绍JSON的结构,在JS中的使用.重点说明JSON如何在.N ...

  5. 在C#中,Json的序列化和反序列化的几种方式总结

    在这篇文章中,我们将会学到如何使用C#,来序列化对象成为Json格式的数据,以及如何反序列化Json数据到对象. 什么是JSON? JSON (JavaScript Object Notation) ...

  6. C#中的Json的序列化和反序列化

    Json是一种通用的数据格式,我们在数据交换的时候,经常会用到,下面介绍c#中的json序列化和反序列化,当然也可用在asp.net,silverlight,wpf中.我们在下面实例讲解如何进行Jso ...

  7. Json的序列化与反序列化

    对于Json的序列化和反序列化,如果自己编写源代码来实现的话,很复杂很烦,所以我采用的是使用别人已经写好的引用文件.这类文件网上有很多,我用的是LitJson,当然Newtonsoft也可以,但后者需 ...

  8. Newtonsoft.Json 的序列化与反序列化

    首先补充一点,Json.Net是支持序列化和反序列化DataTable,DataSet,Entity Framework和NHibernate的.我举例说明DataTable的序列化和反序列化.创建一 ...

  9. ASP.NET MVC 网站开发总结(六)——简谈Json的序列化与反序列化

    首先,先简单的谈一下什么是序列化与反序列化,序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后,可以通 ...

随机推荐

  1. PAT天梯赛练习题——L3-007. 天梯地图(多边权SPFA)

    L3-007. 天梯地图 时间限制 300 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 本题要求你实现一个天梯赛专属在线地图,队员输入自己学校 ...

  2. [COI2007] Patrik 音乐会的等待 (单调栈,模拟)

    题目描述 N个人正在排队进入一个音乐会.人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人.队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的. ...

  3. 转载:lua和c的交互

    extern "C" { #include "lua.h" #include "lualib.h" #include "lauxl ...

  4. git提交之后没有push,代码被覆盖之后恢复

    git  reflog  通过这个看commit id git reset [commit id] --hard   有时候要删除一个index.lock文件.

  5. jQuery事件委托之Safari失效的解决办法--摘抄

    什么是事件委托 事件委托是Jquery中一种事件绑定的方式,不同于常见的事件绑定方式将事件绑定在目标元素上,而是将事件绑定在父级元素上通过事件冒泡来执行绑定函数. //常见的事件绑定(Jquery) ...

  6. hdu4888 多校B 最大流以及最大流唯一判断+输出方案

    题意,给一个矩阵,告诉你每行和.每列和,并且限制所填数不大于k,问矩阵是否唯一. 经典建图不说了,第一次遇到判断最大流唯一性的,学习了:用dfs来判断残网中是否还存在环,若存在,则表明绕这个环走一圈, ...

  7. linux下查看cpu使用情况

    1.在终端输入top 2.ubuntu系统自带有system monitor 3.sudo apt-get install sysstat 然后在终端输入:mpstat

  8. 1.关于无rospy.spin()调用多次callback 2. subscrib后面语句和callback函数运行顺序

    #!/usr/bin/env python import rospy from bzrobot_msgs.msg import bzr_WheelLinearVels #from threading ...

  9. 实验十二 swing图形界面设计

    1.源程序 package information;import java.awt.Container;import java.awt.FlowLayout;import java.awt.event ...

  10. 前端MVC Vue2学习总结(九)——Vuex状态管理插件

    一.概要 1.1.Vuex定义与注意事项 Vuex是为vue.js框架更好的管理状态而设计一个插件.Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的 ...