JSON数据解析
JSON数据解析
本节介绍在 ESP32 上使用 ArduinoJson 库解析 JSON 数据。ESP32 通过 MQTT 接收 JSON 格式的天气数据,需要提取各个值以在电子纸屏幕上显示。完成本节后,你将能够:
- 安装和配置 ArduinoJson 库
- 解析 JSON 字符串以提取特定值
- 处理数值、字符串和嵌套 JSON 字段
- 从解析的 JSON 构建可供显示的数据结构
开始本节前,请确保:
- 已安装 ArduinoJson 库
- 基本理解 JSON 格式
- 已配置 MQTT 通信(参见第 1 章)
天气数据的 JSON 结构
Section titled “天气数据的 JSON 结构”ESP32 通过 MQTT 接收以下格式的 JSON 数据:
{ "temperature": "25.5", "feels_like": "24.0", "humidity": 60, "pressure": 1013, "description": "scattered clouds", "icon": "03d", "city": "London", "timestamp": "2026-05-17T10:30:00.000Z", "error": false}ArduinoJson 库
Section titled “ArduinoJson 库”ArduinoJson 是嵌入式系统中最广泛使用的 JSON 库:
| 特性 | 描述 |
|---|---|
| 内存模型 | 静态(栈)或动态(堆)分配 |
| 版本 6 vs 7 | 均受支持;v7 有改进的 API |
| 最小 RAM | 基本解析约 250 字节 |
| 支持的类型 | 对象、数组、字符串、数字、布尔值、空值 |
内存计算:
// JSON 文档大小取决于复杂度:// 公式:JSON_OBJECT_SIZE(键的数量)// 对于 8 个键:JSON_OBJECT_SIZE(8) ≈ 8 * 2 + 8 = 24 字节(开销)+ 数据
// 建议:使用 ArduinoJson Assistant 计算// https://arduinojson.org/v6/assistant/步骤 1:安装 ArduinoJson 库
Section titled “步骤 1:安装 ArduinoJson 库”PlatformIO 环境(platformio.ini):
lib_deps = bblanchon/ArduinoJson@^7.0.0Arduino IDE 环境:
- 工具 → 管理库
- 搜索 “ArduinoJson” by Benoit Blanchon
- 点击安装
步骤 2:基本 JSON 解析
Section titled “步骤 2:基本 JSON 解析”#include <ArduinoJson.h>
void setup() { Serial.begin(115200);
// 通过 MQTT 接收的示例 JSON 数据 const char* jsonData = "{\"temperature\":\"25.5\",\"humidity\":60,\"description\":\"scattered clouds\",\"city\":\"London\"}";
// 创建一个具有足够容量的 JSON 文档 JsonDocument doc;
// 解析 JSON 字符串 DeserializationError error = deserializeJson(doc, jsonData);
// 检查解析错误 if (error) { Serial.print("JSON 解析失败:"); Serial.println(error.c_str()); return; }
// 提取值 const char* temperature = doc["temperature"]; int humidity = doc["humidity"]; const char* description = doc["description"]; const char* city = doc["city"];
// 显示提取的值 Serial.print("城市:"); Serial.println(city); Serial.print("温度:"); Serial.println(temperature); Serial.print("湿度:"); Serial.println(humidity); Serial.print("描述:"); Serial.println(description);}
void loop() {}步骤 3:在 MQTT 回调中解析
Section titled “步骤 3:在 MQTT 回调中解析”将 JSON 解析与 MQTT 回调函数集成:
#include <WiFi.h>#include <PubSubClient.h>#include <ArduinoJson.h>
// WiFi 和 MQTT 配置const char* ssid = "YOUR_SSID";const char* password = "YOUR_PASSWORD";const char* mqtt_server = "192.168.1.100";const char* mqtt_topic = "factory/weather/data";
WiFiClient espClient;PubSubClient client(espClient);
// 显示数据结构struct WeatherData { char temperature[8]; int humidity; char description[32]; char city[32]; bool valid;};
WeatherData currentWeather = { "", 0, "", "", false };
void callback(char* topic, byte* payload, unsigned int length) { // 将载荷转换为以 null 结尾的字符串 char jsonBuffer[256]; if (length >= 256) { Serial.println("载荷太大!"); return; } memcpy(jsonBuffer, payload, length); jsonBuffer[length] = '\0';
// 解析 JSON JsonDocument doc; DeserializationError error = deserializeJson(doc, jsonBuffer);
if (error) { Serial.print("JSON 解析错误:"); Serial.println(error.c_str()); currentWeather.valid = false; return; }
// 提取值并设置默认回退值 strlcpy(currentWeather.temperature, doc["temperature"] | "--.-", sizeof(currentWeather.temperature));
currentWeather.humidity = doc["humidity"] | 0;
strlcpy(currentWeather.description, doc["description"] | "Unknown", sizeof(currentWeather.description));
strlcpy(currentWeather.city, doc["city"] | "Unknown", sizeof(currentWeather.city));
currentWeather.valid = true;
// 记录解析的数据 Serial.println("天气数据已更新:"); Serial.print(" - 温度:"); Serial.println(currentWeather.temperature); Serial.print(" - 湿度:"); Serial.println(currentWeather.humidity); Serial.print(" - 状况:"); Serial.println(currentWeather.description);}
void setup() { Serial.begin(115200);
// 设置 WiFi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("WiFi 已连接");
// 设置 MQTT client.setServer(mqtt_server, 1883); client.setCallback(callback);
// 连接并订阅 client.connect("ESP32_Display"); client.subscribe(mqtt_topic);}
void loop() { if (!client.connected()) { // 重连逻辑 } client.loop();}步骤 4:解析嵌套对象
Section titled “步骤 4:解析嵌套对象”某些 API 返回嵌套 JSON。处理嵌套对象:
{ "main": { "temp": 25.5, "humidity": 60, "pressure": 1013 }, "weather": [ { "description": "scattered clouds", "icon": "03d" } ], "name": "London"}解析嵌套数据:
void parseNestedJSON(const char* json) { JsonDocument doc; DeserializationError error = deserializeJson(doc, json);
if (error) return;
// 访问嵌套对象 float temp = doc["main"]["temp"]; int humidity = doc["main"]["humidity"];
// 访问数组元素 const char* description = doc["weather"][0]["description"]; const char* icon = doc["weather"][0]["icon"];
// 访问顶级字段 const char* city = doc["name"];
Serial.print(city); Serial.print(":"); Serial.print(temp); Serial.print("°C,"); Serial.println(description);}步骤 5:构建显示字符串
Section titled “步骤 5:构建显示字符串”将解析的 JSON 数据转换为可供显示的字符串:
void updateDisplayFromJSON() { if (!currentWeather.valid) { displayError("No Data"); return; }
display.fillScreen(GxEPD_WHITE);
// 显示城市名称 display.setFont(&FreeMonoBold12pt7b); display.setCursor(5, 25); display.print(currentWeather.city);
// 显示温度(大号) display.setFont(&FreeMonoBold24pt7b); display.setCursor(5, 65);
// 构建温度字符串 char tempStr[16]; snprintf(tempStr, sizeof(tempStr), "%s°C", currentWeather.temperature); display.print(tempStr);
// 显示状况 display.setFont(&FreeMono9pt7b); display.setCursor(5, 95); display.print(currentWeather.description);
// 显示湿度 char humStr[32]; snprintf(humStr, sizeof(humStr), "湿度:%d%%", currentWeather.humidity); display.setCursor(5, 115); display.print(humStr);
display.display();}使用 ArduinoJson Assistant 计算所需内存:
- 访问 ArduinoJson Assistant
- 粘贴你的 JSON 文档
- 工具将计算:
- 所需的
JsonDocument大小 - 推荐的分配方法
- 所需的
示例:
// 对于包含 8 个字段的天气 JSON:// 建议:JsonDocument 256 字节
// 静态分配(栈)— 更快,大小有限StaticJsonDocument<256> doc;
// 动态分配(堆)— 更大,稍慢DynamicJsonDocument doc(1024);- JSON 字符串解析无错误
- 温度值正确提取
- 湿度整数值正确提取
- 字符串字段(描述、城市)未被截断
- 显示屏使用解析的数据更新
- 错误处理对无效 JSON 有效
问题 1:解析返回空值
Section titled “问题 1:解析返回空值”症状:
温度:(空)解决方案:
- 验证 JSON 键名是否完全匹配(区分大小写)
- 检查 JSON 结构是否与预期不同
- 在解析前打印原始 JSON 以验证
问题 2:内存分配失败
Section titled “问题 2:内存分配失败”症状:
DeserializationError::NoMemory解决方案:
- 增大 JsonDocument 大小
- 对较大载荷使用
DynamicJsonDocument - 减少正在提取的字段数量
问题 3:数值不正确
Section titled “问题 3:数值不正确”症状:
- 温度读数为 0 或乱码
解决方案:
- 检查值是字符串(
"25.5")还是数字(25.5)
// 如果温度在 JSON 中是字符串float temp = atof(doc["temperature"]);
// 如果温度在 JSON 中是数字float temp = doc["temperature"].as<float>();- ✅ 在使用解析的数据前始终检查 DeserializationError
- ✅ 使用
|运算符为缺失字段提供默认值 - ✅ 限制字符串缓冲区大小以匹配显示能力
- ❌ 不要在栈上分配大型 JSON 文档——使用 DynamicJsonDocument
- ❌ 如果只需要几个字段,避免解析整个 JSON
- ❌ 不要忽略解析错误——它们指示数据质量问题
- ArduinoJson 是 ESP32 的标准 JSON 库——推荐 v7 版本
- 始终检查解析错误——无效 JSON 不应更新显示
- 使用
doc["key"] | default语法进行安全的字段提取 - 嵌套 JSON 需要链式访问:
doc["parent"]["child"] - 数组访问使用索引:
doc["array"][0] - 内存规划至关重要——使用 ArduinoJson Assistant 进行精确容量计算