官方关于JSON序列化的文档:JSON and serialization
大致意思是 Flutter 没有类似 Gson 的 JSON 序列化库,那样需要借助反射机制知晓运行时类型,Flutter应用会对多余的代码进行优化,这种优化机制与反射冲突,所以只能借助注解编译生成序列化的代码。 Dart内置的 convert 包,有两个函数是用来序列化和反序列化的:
jsonEncode()
jsonDecode()
但是这两个函数是转化 String 和 Map,即
String jsonEncode(Map)
Map jsonDecode(String)
我这里写的函数声明类型跟实际不一样,但是 jsonEncode() 只有传入 Map才能转化成功,基本就是 Map 对应JSON对象,List 对应JSON数组,Dart最开始是携带JS基因的,但是又是强类型语言,Map 类的字面量十分接近JSON,如果要将JSON字符串转成自定义的类,就要编写对应的函数:
//构造函数
ClassName.fromJson(Map json)
//实例方法
Map toJson()
下面是高德web服务API查询天气获取的JSON数据:
{
"status": "1",
"count": "1",
"info": "OK",
"infocode": "10000",
"forecasts": [
{
"city": "深圳市",
"adcode": "440300",
"province": "广东",
"reporttime": "2022-07-09 16:00:47",
"casts": [
{
"date": "2022-07-09",
"week": "6",
"dayweather": "多云",
"nightweather": "多云",
"daytemp": "33",
"nighttemp": "27",
"daywind": "北",
"nightwind": "北",
"daypower": "≤3",
"nightpower": "≤3"
},
{
"date": "2022-07-10",
"week": "7",
"dayweather": "多云",
"nightweather": "多云",
"daytemp": "33",
"nighttemp": "27",
"daywind": "北",
"nightwind": "北",
"daypower": "≤3",
"nightpower": "≤3"
},
{
"date": "2022-07-11",
"week": "1",
"dayweather": "晴",
"nightweather": "晴",
"daytemp": "33",
"nighttemp": "27",
"daywind": "北",
"nightwind": "北",
"daypower": "≤3",
"nightpower": "≤3"
},
{
"date": "2022-07-12",
"week": "2",
"dayweather": "晴",
"nightweather": "晴",
"daytemp": "33",
"nighttemp": "27",
"daywind": "北",
"nightwind": "北",
"daypower": "≤3",
"nightpower": "≤3"
}
]
}
]
}
对应的数据类(手动编写JSON序列化代码):
import 'dart:convert';
//手动编写JSON序列化的代码
class Weather {
String status = '';
String count = '';
String info = '';
String infocode = '';
List forecasts = [];
Weather.fromJson(String json) {
var result = jsonDecode(
json /*, reviver: (key, value) {
print('$key=$value ${value.runtimeType}');
}*/
);
// print('result.runtimeType=${result.runtimeType}'); // Map
status = result['status'];
count = result['count'];
info = result['info'];
infocode = result['infocode'];
// print(result['forecasts'].runtimeType);// List
forecasts = (result['forecasts'] as List)
.map((e) => Forecast.fromMap(e as Map))
.toList();
}
Map toJson() {
return {
'status': status,
'count': count,
'info': info,
'infocode': infocode,
'forecasts': forecasts.map((e) => e.toJson()).toList(),
};
}
}
class Forecast {
String city = '';
String adcode = '';
String province = '';
String reporttime = '';
List casts = [];
Forecast.fromMap(Map map) {
city = map["city"];
adcode = map["adcode"];
province = map["province"];
reporttime = map["reporttime"];
casts = (map["casts"] as List)
.map((e) => Cast.fromMap(e as Map))
.toList();
}
Map toJson() {
return {
'city': city,
'adcode': adcode,
'province': province,
'reporttime': reporttime,
'casts': casts.map((e) => e.toJson()).toList(),
};
}
}
class Cast {
String date = '';
String week = '';
String dayweather = '';
String daytemp = '';
String nighttemp = '';
String daywind = '';
String nightwind = '';
String daypower = '';
String nightpower = '';
Cast.fromMap(Map map) {
date = map['date'];
week = map['week'];
dayweather = map['dayweather'];
daytemp = map['daytemp'];
nighttemp = map['nighttemp'];
daywind = map['daywind'];
nightwind = map['nightwind'];
daypower = map['daypower'];
nightpower = map['nightpower'];
}
Map toJson() {
return {
'date': date,
'week': week,
'dayweather': dayweather,
'daytemp': daytemp,
'nighttemp': nighttemp,
'daywind': daywind,
'nightwind': nightwind,
'daypower': daypower,
'nightpower': nightpower,
};
}
}
这里使用的 JSON 数据已经算比较复杂了,如果实际开发中使用更复杂的数据,手动编写就很繁琐了,这时候就要借助工具来生成样板代码,生成样板代码需要在 pubspec.yaml 添加如下依赖:
dependencies:
...
json_serializable: ^6.3.1
dev_dependencies:
...
build_runner: ^2.1.11
按照官方文档中的示例编写如下的数据类代码(IDE会有红色提示,先忽略,运行本文后面提到的 flutter 指令后会生成缺失的方法),其中两个类的注解带有explicitToJson 参数@JsonSerializable(explicitToJson: true),这样是为了解析嵌套的类:
import 'package:json_annotation/json_annotation.dart';
/// This allows the `WeatherX` 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 'weather_x.g.dart';
/// An annotation for the code generator to know that this class needs the
/// JSON serialization logic to be generated.
/// explicitToJson: true 是为了解析嵌套的类
@JsonSerializable(explicitToJson: true)
class WeatherX {
String status = '';
String count = '';
String info = '';
String infocode = '';
List forecasts = [];
WeatherX(this.status, this.count, this.info, this.infocode, this.forecasts);
/// A necessary factory constructor for creating a new WeatherX instance
/// from a map. Pass the map to the generated `_$WeatherXFromJson()` constructor.
/// The constructor is named after the source class, in this case, WeatherX.
factory WeatherX.fromJson(Map json) =>
_$WeatherXFromJson(json);
/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$WeatherXToJson`.
Map toJson() => _$WeatherXToJson(this);
}
@JsonSerializable(explicitToJson: true)
class ForecastX {
String city = '';
String adcode = '';
String province = '';
String reporttime = '';
List casts = [];
ForecastX(this.city, this.adcode, this.province, this.reporttime, this.casts);
factory ForecastX.fromJson(Map json) =>
_$ForecastXFromJson(json);
Map toJson() => _$ForecastXToJson(this);
}
@JsonSerializable()
class CastX {
String date = '';
String week = '';
String dayweather = '';
String daytemp = '';
String nighttemp = '';
String daywind = '';
String nightwind = '';
String daypower = '';
String nightpower = '';
CastX(this.date, this.week, this.dayweather, this.daytemp, this.nighttemp,
this.daywind, this.nightwind, this.daypower, this.nightpower);
factory CastX.fromJson(Map json) =>
_$CastXFromJson(json);
Map toJson() => _$CastXToJson(this);
}
然后在工程根目录执行下面的指令:
flutter pub run build_runner build
命令执行成功,就会生成 xxx.g.dart 文件,里面包含了生成的样板代码,之前编写的数据类也不再提示错误,最后就可以使用下面的代码进行JSON序列化和反序列化:
import 'dart:convert';
import '../weather_x.dart';
// JSON 字符串转 Dart 类实例
WeatherX weatherX = WeatherX.fromJson(jsonDecode(jsonString));
// 类实例转 JSON 字符串
String jsonString = jsonEncode(weatherX);