鸿蒙Flutter JSON解析与序列化:手动解析JSON数据



概述
在Flutter开发中,手动解析JSON是最基础也是最重要的技能之一。特别是在鸿蒙平台上,当获取到网络API返回的JSON数据后,需要手动将其转换为Dart对象进行处理。本文将详细介绍手动解析JSON的步骤和技巧,结合天气查询应用的实际场景,帮助开发者掌握手动解析的方法。
1. 手动解析的基本概念
手动解析JSON就是通过代码一步步地将JSON字符串转换为Dart对象的过程。这个过程主要包括以下步骤:
- 导入dart:convert库:使用内置的JSON解析能力
- 解析JSON字符串:将字符串转换为Map或List
- 提取数据:通过键名获取对应的值
- 类型转换:将dynamic类型转换为具体类型
- 处理嵌套结构:递归处理嵌套的对象和数组
1.1 手动解析的优缺点
| 优点 | 缺点 |
|---|---|
| 灵活性高,可自定义解析逻辑 | 代码量大,重复工作多 |
| 无需额外依赖和配置 | 容易出错,需要大量类型转换 |
| 适用于简单JSON结构 | 复杂结构维护困难 |
| 便于调试和理解 | 性能相对较低 |
2. 手动解析的核心步骤
2.1 第一步:导入dart:convert库
import 'dart:convert';
dart:convert 是Dart标准库的一部分,提供了JSON编码和解码的功能,无需额外安装依赖。
2.2 第二步:解析JSON字符串
使用 json.decode() 方法将JSON字符串转换为Dart对象:
String jsonString = '''
{
"city": "北京",
"temperature": 28,
"humidity": 65
}
''';
Map<String, dynamic> data = json.decode(jsonString);
注意事项:
json.decode()返回的类型是dynamic,需要根据实际情况进行类型转换- 如果JSON字符串格式不正确,会抛出
FormatException - 顶层结构可能是
Map<String, dynamic>或List<dynamic>
2.3 第三步:提取数据并类型转换
String city = data['city'] as String;
int temperature = data['temperature'] as int;
int humidity = data['humidity'] as int;
bool isRainy = data['isRainy'] as bool;
String updateTime = data['updateTime'] as String;
类型转换策略:
| JSON类型 | Dart类型 | 转换方式 |
|---|---|---|
| 字符串 | String | as String |
| 整数 | int | as int |
| 浮点数 | double | as double |
| 布尔 | bool | as bool |
| 对象 | Map<String, dynamic> | as Map<String, dynamic> |
| 数组 | List | as List<dynamic> |
| null | null | 直接使用或判空 |
2.4 第四步:处理嵌套对象
当JSON中包含嵌套对象时,需要逐层解析:
Map<String, dynamic> wind = data['wind'] as Map<String, dynamic>;
String windDirection = wind['direction'] as String;
int windSpeed = wind['speed'] as int;
2.5 第五步:处理数组
数组的处理需要遍历并转换每个元素:
List<dynamic> forecastList = data['forecast'] as List<dynamic>;
List<String> forecasts = forecastList.map((item) {
final Map<String, dynamic> forecast = item as Map<String, dynamic>;
return '${forecast['date']}: 最高${forecast['high']}° / 最低${forecast['low']}°';
}).toList();
3. 完整示例:天气数据解析
下面是一个完整的天气数据手动解析示例:
import 'dart:convert';
void main() {
String jsonString = '''
{
"city": "北京",
"temperature": 28,
"humidity": 65,
"wind": {
"direction": "东南风",
"speed": 3
},
"forecast": [
{"date": "周一", "high": 30, "low": 22},
{"date": "周二", "high": 29, "low": 21},
{"date": "周三", "high": 31, "low": 23}
],
"isRainy": false,
"updateTime": "2024-07-22 14:30"
}
''';
// 解析顶层对象
Map<String, dynamic> weatherData = json.decode(jsonString) as Map<String, dynamic>;
// 提取基础数据
String city = weatherData['city'] as String;
int temperature = weatherData['temperature'] as int;
int humidity = weatherData['humidity'] as int;
bool isRainy = weatherData['isRainy'] as bool;
String updateTime = weatherData['updateTime'] as String;
// 解析嵌套的wind对象
Map<String, dynamic> wind = weatherData['wind'] as Map<String, dynamic>;
String windDirection = wind['direction'] as String;
int windSpeed = wind['speed'] as int;
// 解析forecast数组
List<dynamic> forecastList = weatherData['forecast'] as List<dynamic>;
List<String> forecasts = forecastList.map((item) {
final Map<String, dynamic> forecast = item as Map<String, dynamic>;
return '${forecast['date']}: 最高${forecast['high']}° / 最低${forecast['low']}°';
}).toList();
// 输出解析结果
print('🌤️ 城市: $city');
print('🌡️ 温度: ${temperature}°C');
print('💧 湿度: ${humidity}%');
print('💨 风向: $windDirection ${windSpeed}级');
print('🌧️ 是否下雨: ${isRainy ? '是' : '否'}');
print('⏰ 更新时间: $updateTime');
print('\n📅 天气预报:');
forecasts.forEach(print);
}
4. 错误处理策略
在手动解析过程中,可能会遇到各种错误,需要进行适当的处理。
4.1 格式错误处理
try {
Map<String, dynamic> data = json.decode(jsonString);
} on FormatException catch (e) {
print('JSON格式错误: $e');
}
4.2 类型转换错误处理
try {
int temperature = data['temperature'] as int;
} on TypeError catch (e) {
print('类型转换错误: $e');
}
4.3 空值处理
String? description = data['description'] as String?;
String safeDescription = description ?? '暂无描述';
4.4 完整错误处理示例
void parseJson(String jsonString) {
try {
Map<String, dynamic> data = json.decode(jsonString);
String city = data['city'] as String? ?? '未知城市';
int temperature = (data['temperature'] as num?)?.toInt() ?? 0;
int humidity = (data['humidity'] as num?)?.toInt() ?? 0;
print('城市: $city, 温度: ${temperature}°C, 湿度: ${humidity}%');
} on FormatException catch (e) {
print('JSON格式错误: $e');
} on TypeError catch (e) {
print('类型转换错误: $e');
} catch (e) {
print('未知错误: $e');
}
}
5. 鸿蒙平台注意事项
在鸿蒙平台上使用手动解析时,需要注意以下几点:
5.1 编码问题
确保JSON字符串使用UTF-8编码:
import 'dart:convert';
// 确保响应体使用UTF-8解码
String jsonString = utf8.decode(response.bodyBytes);
5.2 平台差异
虽然JSON解析在各平台上基本一致,但在处理特定数据格式时需要注意:
- 日期时间格式可能因平台而异
- 某些数值类型在不同平台上的精度可能不同
- 需要处理平台特定的特殊字符
5.3 性能优化
在鸿蒙平台上,为了提高解析性能,可以:
- 使用
jsonDecode而不是json.decode(前者是推荐的命名方式) - 避免在热路径中频繁解析JSON
- 使用缓存机制减少重复解析
6. 手动解析的适用场景
手动解析适用于以下场景:
- 简单JSON结构:字段较少、层级较浅的JSON数据
- 快速原型开发:需要快速实现功能验证
- 学习和理解:帮助理解JSON解析的底层原理
- 特殊解析逻辑:需要自定义解析规则的场景
7. 手动解析与自动解析的对比
| 特性 | 手动解析 | 自动解析(json_serializable) |
|---|---|---|
| 代码量 | 多 | 少 |
| 灵活性 | 高 | 低 |
| 错误率 | 高 | 低 |
| 开发效率 | 低 | 高 |
| 维护成本 | 高 | 低 |
| 学习成本 | 低 | 高 |
| 适用场景 | 简单结构 | 复杂结构 |
8. 最佳实践建议
8.1 使用类型断言
始终使用 as 进行类型转换,避免运行时错误:
String city = data['city'] as String; // 推荐
String city = data['city']; // 不推荐,可能导致类型错误
8.2 处理可能为null的字段
使用空值安全操作符处理可选字段:
String? description = data['description'] as String?;
String safeDescription = description ?? '暂无描述';
8.3 使用num处理数字类型
JSON中的数字可能是int或double,使用num统一处理:
num tempNum = data['temperature'] as num;
int temperature = tempNum.toInt();
double tempDouble = tempNum.toDouble();
8.4 封装解析逻辑
将解析逻辑封装到数据模型类中:
class Weather {
final String city;
final int temperature;
Weather({required this.city, required this.temperature});
factory Weather.fromJson(Map<String, dynamic> json) {
return Weather(
city: json['city'] as String,
temperature: (json['temperature'] as num).toInt(),
);
}
}
9. 常见问题与解决方案
9.1 JSON格式错误
问题:解析时抛出 FormatException
解决方案:
- 检查JSON字符串是否符合语法规范
- 确保所有字符串使用双引号
- 检查是否有多余或缺失的逗号
- 使用在线JSON校验工具验证
9.2 类型转换失败
问题:抛出 TypeError
解决方案:
- 确认字段类型与预期一致
- 使用
as?进行安全转换 - 添加类型检查逻辑
9.3 字段缺失
问题:访问不存在的键返回null
解决方案:
- 使用空值安全操作符
?? - 在访问前检查字段是否存在
- 提供默认值
9.4 嵌套过深
问题:多层嵌套导致代码冗长
解决方案:
- 拆分为多个数据模型类
- 使用递归解析方法
- 考虑使用自动解析工具
10. 总结
手动解析JSON是Flutter开发的基础技能,虽然代码量较大,但对于理解JSON解析的原理非常有帮助。在实际项目中,对于简单的JSON结构可以使用手动解析,而对于复杂结构则推荐使用自动解析工具。下一章将介绍 dart:convert 库的更多用法。
核心知识点回顾
- 手动解析需要导入
dart:convert库 - 使用
json.decode()将JSON字符串转换为Dart对象 - 通过键名访问值并进行类型转换
- 使用
as进行类型断言,使用??处理空值 - 嵌套对象和数组需要逐层解析
- 必须添加错误处理逻辑,捕获
FormatException和TypeError - 推荐将解析逻辑封装到数据模型类的
fromJson工厂方法中
更多推荐



所有评论(0)