跳转到内容

JSON数据解析

JSON数据解析

本节介绍在 ESP32 上使用 ArduinoJson 库解析 JSON 数据。ESP32 通过 MQTT 接收 JSON 格式的天气数据,需要提取各个值以在电子纸屏幕上显示。完成本节后,你将能够:

  • 安装和配置 ArduinoJson 库
  • 解析 JSON 字符串以提取特定值
  • 处理数值、字符串和嵌套 JSON 字段
  • 从解析的 JSON 构建可供显示的数据结构

开始本节前,请确保:

  • 已安装 ArduinoJson 库
  • 基本理解 JSON 格式
  • 已配置 MQTT 通信(参见第 1 章)

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 是嵌入式系统中最广泛使用的 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/

PlatformIO 环境platformio.ini):

lib_deps =
bblanchon/ArduinoJson@^7.0.0

Arduino IDE 环境

  1. 工具 → 管理库
  2. 搜索 “ArduinoJson” by Benoit Blanchon
  3. 点击安装
#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() {}

将 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();
}

某些 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);
}

将解析的 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 计算所需内存:

  1. 访问 ArduinoJson Assistant
  2. 粘贴你的 JSON 文档
  3. 工具将计算:
    • 所需的 JsonDocument 大小
    • 推荐的分配方法

示例

// 对于包含 8 个字段的天气 JSON:
// 建议:JsonDocument 256 字节
// 静态分配(栈)— 更快,大小有限
StaticJsonDocument<256> doc;
// 动态分配(堆)— 更大,稍慢
DynamicJsonDocument doc(1024);
  • JSON 字符串解析无错误
  • 温度值正确提取
  • 湿度整数值正确提取
  • 字符串字段(描述、城市)未被截断
  • 显示屏使用解析的数据更新
  • 错误处理对无效 JSON 有效

症状

温度:(空)

解决方案

  • 验证 JSON 键名是否完全匹配(区分大小写)
  • 检查 JSON 结构是否与预期不同
  • 在解析前打印原始 JSON 以验证

症状

DeserializationError::NoMemory

解决方案

  • 增大 JsonDocument 大小
  • 对较大载荷使用 DynamicJsonDocument
  • 减少正在提取的字段数量

症状

  • 温度读数为 0 或乱码

解决方案

  • 检查值是字符串("25.5")还是数字(25.5
// 如果温度在 JSON 中是字符串
float temp = atof(doc["temperature"]);
// 如果温度在 JSON 中是数字
float temp = doc["temperature"].as<float>();
  • 在使用解析的数据前始终检查 DeserializationError
  • 使用 | 运算符为缺失字段提供默认值
  • 限制字符串缓冲区大小以匹配显示能力
  • 不要在栈上分配大型 JSON 文档——使用 DynamicJsonDocument
  • 如果只需要几个字段,避免解析整个 JSON
  • 不要忽略解析错误——它们指示数据质量问题
  1. ArduinoJson 是 ESP32 的标准 JSON 库——推荐 v7 版本
  2. 始终检查解析错误——无效 JSON 不应更新显示
  3. 使用 doc["key"] | default 语法进行安全的字段提取
  4. 嵌套 JSON 需要链式访问:doc["parent"]["child"]
  5. 数组访问使用索引:doc["array"][0]
  6. 内存规划至关重要——使用 ArduinoJson Assistant 进行精确容量计算