Flutter 使用合理的 null 安全性将多级 JSON 解析为类
Posted
技术标签:
【中文标题】Flutter 使用合理的 null 安全性将多级 JSON 解析为类【英文标题】:Flutter parsing multilevel JSON to a class using sound null safety 【发布时间】:2021-08-01 11:43:27 【问题描述】:我在 android Studio 中使用 FLutter,将多级 JSON 解析为一个类。尽管我检查了许多 *** 帖子,但我找不到解决此问题的方法。 这是我的 JSON
String myJson = '
[
"sensor":"1234",
"data":
[
"value":"40",
"reading_time1":"2021-04-10"
,
"value":"38",
"reading_time1":"2021-04-11"
]
,
"sensor":"1235",
"data":
[
"value":"2",
"reading_time1":"2021-04-23"
,
"value":"39",
"reading_time1":"2021-04-24"
]
] '
这些是我的类,使用 https://javiercbk.github.io/json_to_dart/ 生成。但是生成的代码不起作用(不是空安全)所以在阅读了很多帖子后我想出了这些:
class AllSensorData
String sensor="";
List<Sensordata> sensordata = [];
AllSensorData(required this.sensor,required this.sensordata);
AllSensorData.fromJson(Map<String, dynamic> json)
sensor = json['sensor'];
if (json['data'] != null)
sensordata = <Sensordata>[];
json['data'].forEach((v)
sensordata.add(new Sensordata.fromJson(v));
);
Map<String, dynamic> toJson()
final Map<String, dynamic> data = new Map<String, dynamic>();
data['sensor'] = this.sensor;
if (this.sensordata != null)
data['data'] = this.sensordata.map((v) => v.toJson()).toList();
return data;
class Sensordata
String value;
String reading_time;
Sensordata(
required this.value,
required this.reading_time
);
Sensordata.fromJson(Map<String, dynamic> json) :
value = json['value'],
reading_time = json['reading_time1'];
Map<String, dynamic> toJson()
final Map<String, dynamic> data = new Map<String, dynamic>();
data['value'] = this.value;
data['reading_time'] = this.reading_time;
return data;
现在当我跑步时:
var decodedJson = jsonDecode(myJson);
var jsonValue = AllSensorData.fromJson(decodedJson);
错误是:
type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'
虽然我怀疑可能还有更多。感谢您的帮助。
【问题讨论】:
decodedJson 实际上是一个 SensorData 列表,而不仅仅是一个。您正在尝试将列表传递给 AllSensorData.fromJson 而它接受地图而不是列表。因此错误。确保其中至少有一个元素后尝试 AllSensorData(decodedJson[0])。 【参考方案1】:对于其他来这里的人来说,这是一个稍微通用的答案。
分析仪选项
当您在 analysis_options.yaml 文件中将 implicit-dynamic
设置为 false
时,一开始可能很难解析 JSON。
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
如何种姓
如果您在 JSON 的顶层使用列表,则可以使用 as List
来强制类型:
import 'dart:convert';
void main()
final myList = json.decode(myJson) as List;
for (final item in myList)
final name = item['name'] as String? ?? '';
final age = item['age'] as int? ?? 0;
String myJson = '''
[
"name":"bob",
"age":22
,
"name":"mary",
"age":35
]
''';
这使您可以遍历每个项目。 item
本身就是 dynamic
,但您仍然可以将其视为 Map
,如果它为 null,则为其提供默认类型值。
使用fromJson
方法
这是一个使用数据类的示例。 (为简洁起见,省略了 import 和 myJson
字符串。)
void main()
final myList = json.decode(myJson) as List;
for (final item in myList)
if (item is! Map<String, dynamic>) return; // <-- interesting part
final person = Person.fromJson(item);
class Person
final String name;
final int age;
Person(this.name, this.age);
factory Person.fromJson(Map<String, dynamic> json)
final name = json['name'] as String? ?? '';
final age = json['age'] as int? ?? 0;
return Person(name, age);
这个几乎做同样的事情,除了在将映射传递给fromJson
方法之前,您检查类型。这允许 Dart 安全地将动态 item
提升为 fromJson
参数所需的 Map<String, dynamic>
类型。
【讨论】:
【参考方案2】:您的代码大部分是正确的。您只需为decodedJson
的每个 元素调用fromJson
,因为它包含许多 sensorMaps 而不是一个。
List<Map<String,dynamic>> decodedJson = jsonDecode(myJson);
List<AllSensorData> sensorDataList = decodedJson.forEach(
(sensorMap) => AllSensorData.fromJson(sensorMap)
);
在调用 toJson
时,您也会这样做。
【讨论】:
那是因为在你的类方法 fromJson 你没有指定返回类型。【参考方案3】:正如其他人所指出的,JSON 有一个传感器数据列表,因此您需要对列表元素进行解码。我使用quicktype 生成(正确的)代码。
您说您正在使用null safety
,但您的类中没有任何可选数据字段。如果数据中的字段是可选的,那么您可以使用quicktype
选项Make all properties optional
。不幸的是,quicktype
似乎不知道dart
中的空安全性,因此您必须编辑其生成的代码以在数据字段声明中添加?
,并在引用中添加!
。我在下面做了这个:
import 'dart:convert';
List<Sensordata> sensordataFromJson(String str) =>
List<Sensordata>.from(json.decode(str).map((x) => Sensordata.fromJson(x)));
String sensordataToJson(List<Sensordata> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Sensordata
Sensordata(
this.sensor,
this.data,
);
String? sensor;
List<Datum>? data;
factory Sensordata.fromJson(Map<String, dynamic> json) => Sensordata(
sensor: json["sensor"] == null ? null : json["sensor"],
data: json["data"] == null
? null
: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() =>
"sensor": sensor == null ? null : sensor,
"data": data == null
? null
: List<dynamic>.from(data!.map((x) => x.toJson())),
;
class Datum
Datum(
this.value,
this.readingTime1,
);
String? value;
DateTime? readingTime1;
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
value: json["value"] == null ? null : json["value"],
readingTime1: json["reading_time1"] == null
? null
: DateTime.parse(json["reading_time1"]),
);
Map<String, dynamic> toJson() =>
"value": value == null ? null : value,
"reading_time1": readingTime1 == null
? null
: "$readingTime1!.year.toString().padLeft(4, '0')-$readingTime1!.month.toString().padLeft(2, '0')-$readingTime1!.day.toString().padLeft(2, '0')",
;
【讨论】:
【参考方案4】:您的 JSON 数据是 List<dynamic>
,因此 decodedJson
也是 List<dynamic>
。
您必须从 decodedJson
的每个元素中解码 json。
var decodedJson = jsonDecode(myJson);
List<AllSensorData> list = [];
for (Map<String, dynamic> json in decodedJson)
list.add(AllSensorData.fromJson(json));
如果您有任何疑问,请发表评论。
【讨论】:
感谢您的回答。我尝试使用代码,但在运行时出现错误:处理手势时抛出以下_TypeError:类型'Null'不是调试此行的'String'类型的子类型:list.add(AllSensorData. fromJson(json)); 当 AllSensorData 中的某个字段从 API 中获得 null 值时,会发生此错误。您需要检查 fromJson 方法中是否有任何值是否为 null 并根据该值返回。以上是关于Flutter 使用合理的 null 安全性将多级 JSON 解析为类的主要内容,如果未能解决你的问题,请参考以下文章
snapshot.data.length 中的 Flutter Null 安全错误
如何使用具有 Null 安全性的 FlutterFire Firestore 和 Flutter 2.0 从 Firebase 获取数据