使用声音空安全将多级JSON解析为class
Flutter parsing multilevel JSON to a class using sound null safety
我在 Android studio 中使用 FLutter,将多级 JSON 解析为 class。虽然我检查了很多 Whosebug post,但我找不到解决这个问题的方法。
这是我的 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"
}
]
}
] '
这些是我的 classes,使用 https://javiercbk.github.io/json_to_dart/ 生成。但是生成的代码不起作用(不是 null 安全)所以在阅读了许多 post 之后我想出了这些:
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>'
尽管我怀疑可能还有更多。感谢您的帮助。
您的 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));
}
如有任何疑问,请评论。
正如其他人指出的那样,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')}",
};
}
您的代码大部分是正确的。您只需要为 decodedJson
的每个 元素调用 fromJson
因为它包含 许多 个 sensorMaps 而不是一个。
List<Map<String,dynamic>> decodedJson = jsonDecode(myJson);
List<AllSensorData> sensorDataList = decodedJson.forEach(
(sensorMap) => AllSensorData.fromJson(sensorMap)
);
您在调用 toJson
时将执行相同的操作。
对于来到这里的其他人来说,这是一个稍微更通用的答案。
分析器选项
当您在 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
方法
这里是一个使用数据 class 的例子。 (为简洁起见,省略了导入和 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>
类型。
我在 Android studio 中使用 FLutter,将多级 JSON 解析为 class。虽然我检查了很多 Whosebug post,但我找不到解决这个问题的方法。 这是我的 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"
}
]
}
] '
这些是我的 classes,使用 https://javiercbk.github.io/json_to_dart/ 生成。但是生成的代码不起作用(不是 null 安全)所以在阅读了许多 post 之后我想出了这些:
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>'
尽管我怀疑可能还有更多。感谢您的帮助。
您的 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));
}
如有任何疑问,请评论。
正如其他人指出的那样,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')}",
};
}
您的代码大部分是正确的。您只需要为 decodedJson
的每个 元素调用 fromJson
因为它包含 许多 个 sensorMaps 而不是一个。
List<Map<String,dynamic>> decodedJson = jsonDecode(myJson);
List<AllSensorData> sensorDataList = decodedJson.forEach(
(sensorMap) => AllSensorData.fromJson(sensorMap)
);
您在调用 toJson
时将执行相同的操作。
对于来到这里的其他人来说,这是一个稍微更通用的答案。
分析器选项
当您在 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
方法
这里是一个使用数据 class 的例子。 (为简洁起见,省略了导入和 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>
类型。