详细介绍:ESP32 第四节

HTTP 返回的 JSON 怎么解析?(ArduinoJson 入门 + 知识点讲解)

前面几节,我们已经做了这些事:

  • 会让 ESP32 连上 WiFi

  • 知道怎么用 HTTPClient 发一个 GET / POST 请求

  • 也理解了 http.getString() 得到的 payload 其实就是服务器返回的“正文内容”字符串

但接下来你一定会遇到一个现实问题:

服务器返回的是 JSON 字符串,我要的只是里面几个数,
难道一直在串口上看着字符串,用眼睛数吗?

这节课就解决这一件事:

把 HTTP 返回的 JSON 字符串,变成你能直接用的变量。


一、先搞清楚:JSON 长什么样、为什么要解析?

1.1 JSON 是什么?

JSON = JavaScript Object Notation,本质就是:

一种用来传输数据的文本格式,人和机器都能看懂。

你在各种 Web API 里看到的,都很像这样:

{
  "temp": 25.3,
  "humi": 60.1,
  "device": "esp32-devkit",
  "ok": true
}

或者复杂一点:

{
  "sensor": {
    "temp": 25.3,
    "humi": 60.1
  },
  "tags": ["lab", "test"],
  "count": 3
}

这里有几类基本结构:

  • 对象(object):用 {} 包起来,里面是 "键": 值

  • 数组(array):用 [] 包起来,里面是一串值

  • 值(value):可以是数字、字符串、布尔、null、对象、数组

1.2 为什么要“解析 JSON”?

因为 ESP32 拿到的是一整串字符串,比如:

String payload = http.getString();

假设内容是:

{"temp":25.3,"humi":60.1,"ok":true}

你真正想要的是:

float temp = 25.3;
float humi = 60.1;
bool  ok   = true;

也就是:

从字符串里面,把某些字段挖出来,放进变量里用。

这个过程就叫 “JSON 解析(parse)”


二、ESP32 解析 JSON 的常见方案:直接用 ArduinoJson

理论上,你可以自己写字符串处理:

  • 一顿 indexOf / substring / 手搓解析器

  • 非要自己写也能写,但:

    • 一复杂就容易写崩

    • 调试很痛苦

    • 任何 JSON 结构变化你都要改解析逻辑

更靠谱的做法是:用现成的 JSON 库

在 Arduino / ESP32 圈子里,最常用的就是:

ArduinoJson(作者:Benoit Blanchon)

特点:

  • 文档多、例子多

  • 支持对象、数组、嵌套各种情况

  • 适配 Arduino / ESP32,很成熟

2.1 在 PlatformIO 里添加 ArduinoJson

你的工程 platformio.ini 一般长这样(示例):

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino

在后面加上:

lib_deps =
  bblanchon/ArduinoJson @ ^7.0.0

大概变成:

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
  bblanchon/ArduinoJson @ ^7.0.0

保存后,PlatformIO 会自动下载这个库。

知识点:
lib_deps 就是告诉 PlatformIO“这个工程依赖哪些外部库”,你写上去,它帮你拉。


三、JSON 解析的 3 个核心概念

在 ArduinoJson 里,我们主要记住 3 个东西:

  1. JsonDocument(文档)

    • 它是一个“内存中的 JSON 树”

    • 相当于把字符串解析之后的结果存放在内存里的一棵树

    • 有两种:StaticJsonDocument(静态栈上) 和 DynamicJsonDocument(堆上)

  2. deserializeJson()(反序列化)

    • 把“JSON 字符串” → 解析 → 填入 JsonDocument

    • 如果解析失败,会返回错误

  3. doc["key"] 的方式访问字段

    • doc["temp"].as<float>()

    • doc["ok"].as<bool>()

    • 对于嵌套对象:doc["sensor"]["temp"]

    • 对于数组:doc["tags"][0]

你可以把这三步理解成:

字符串 → 用 deserializeJson() 喂给 doc → 用 doc["xxx"] 拿数据


四、解析一个最简单的 JSON:平铺 key-value

假设服务器返回:

{"temp":25.3,"humi":60.1,"ok":true}

4.1 基本解析流程(知识点 + 模板)

#include 
#include 
// 假设 payload 是 HTTP 返回的 JSON 字符串
String payload = "{\"temp\":25.3,\"humi\":60.1,\"ok\":true}";
void parseSimpleJson(const String& json) {
  // 1. 准备一个 JsonDocument
  //    这里用 DynamicJsonDocument,比 Static 更灵活
  DynamicJsonDocument doc(256);  // 256 字节内存,足够放这个简单 JSON
  // 2. 反序列化:把字符串 -> 解析 -> 填进 doc
  DeserializationError err = deserializeJson(doc, json);
  if (err) {
    Serial.print("[JSON] Parse failed: ");
    Serial.println(err.c_str());
    return;
  }
  // 3. 通过 doc["key"] 拿数据
  float temp = doc["temp"].as();
  float humi = doc["humi"].as();
  bool  ok   = doc["ok"].as();
  // 4. 打印看看
  Serial.print("temp = ");
  Serial.println(temp);
  Serial.print("humi = ");
  Serial.println(humi);
  Serial.print("ok   = ");
  Serial.println(ok ? "true" : "false");
}
void setup() {
  Serial.begin(115200);
  delay(1000);
  parseSimpleJson(payload);
}
void loop() {}

4.2 这段代码你需要记住什么?

  • DynamicJsonDocument doc(256);
    → 申请一个足够大的 JSON 文档缓冲区(256 这数字可以按 JSON 复杂度调)

  • deserializeJson(doc, json);
    → 真正的解析动作

  • doc["temp"].as<float>()
    → 从 JSON 里拿出 temp 这个字段,并转成 float

你不需要记住每个 API 的细节,
只要记住:doc 是树,deserializeJson 把字符串喂进去,doc["key"] 把值拿出来。


五、解析带“嵌套对象”和“数组”的 JSON

现实中,接口返回的数据往往不是简单的平铺,而是类似:

{
  "sensor": {
    "temp": 25.3,
    "humi": 60.1
  },
  "tags": ["lab", "test"],
  "count": 2
}

5.1 对象嵌套访问:doc["sensor"]["temp"]

String payload = R"(
{
  "sensor": {
    "temp": 25.3,
    "humi": 60.1
  },
  "tags": ["lab", "test"],
  "count": 2
}
)";
void parseNestedJson(const String& json) {
  DynamicJsonDocument doc(512);  // 略大一点
  DeserializationError err = deserializeJson(doc, json);
  if (err) {
    Serial.print("[JSON] Parse failed: ");
    Serial.println(err.c_str());
    return;
  }
  // 1. 嵌套对象:sensor 里有 temp/humi
  float temp = doc["sensor"]["temp"].as();
  float humi = doc["sensor"]["humi"].as();
  // 2. 顶层的字段
  int   count = doc["count"].as();
  // 3. 数组:tags 是一个数组
  const char* tag0 = doc["tags"][0];  // "lab"
  const char* tag1 = doc["tags"][1];  // "test"
  Serial.printf("temp=%.2f, humi=%.2f, count=%d\n", temp, humi, count);
  Serial.printf("tag0=%s, tag1=%s\n", tag0, tag1);
}

知识点:

  • 嵌套对象就是多加一层 ["xxx"]
    doc["sensor"]["temp"]

  • JSON 数组和 C 数组类似,[0][1] 下标访问:
    doc["tags"][0]

5.2 遍历数组(知道就行)

如果数组长度不固定,你可以遍历:

JsonArray tags = doc["tags"].as();
for (JsonVariant v : tags) {
  Serial.println(v.as());
}

六、把“HTTP + JSON”连起来:一个完整的知识链

你前面已经会了 HTTP GET,现在把这两块拼一下:

  1. 先用 HTTPClient 请求一个接口,得到 payload(字符串)

  2. 再用 ArduinoJson 去解析这个 payload

大概结构如下(伪代码,偏知识点):

#include 
#include 
#include 
bool httpGetJson(const char* url) {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("[HTTP] WiFi not connected");
    return false;
  }
  HTTPClient http;
  http.begin(url);
  int httpCode = http.GET();
  if (httpCode <= 0) {
    Serial.printf("[HTTP] GET failed, error: %s\n",
                  http.errorToString(httpCode).c_str());
    http.end();
    return false;
  }
  if (httpCode != HTTP_CODE_OK) {
    Serial.printf("[HTTP] Unexpected code: %d\n", httpCode);
    http.end();
    return false;
  }
  String payload = http.getString();
  http.end();
  // 这里 payload 就是 JSON 文本字符串
  Serial.println("[HTTP] Payload:");
  Serial.println(payload);
  // ========= JSON 解析 =========
  DynamicJsonDocument doc(512);
  DeserializationError err = deserializeJson(doc, payload);
  if (err) {
    Serial.print("[JSON] Parse failed: ");
    Serial.println(err.c_str());
    return false;
  }
  // 根据实际 JSON 结构,取字段:
  // 例如服务器返回:{"temp":25.3,"humi":60.1}
  float temp = doc["temp"].as();
  float humi = doc["humi"].as();
  Serial.printf("Parsed: temp=%.2f, humi=%.2f\n", temp, humi);
  return true;
}

你会发现:

  • HTTP 部分:只负责“从网络拿字符串”

  • JSON 部分:只负责“把字符串解析成变量”

这就是一个很标准的:
网络 I/O 层 + 数据解析层 的分离。
以后你换接口、换服务器,只要改 JSON 字段名就行。


七、关于 JsonDocument 内存大小怎么估

这部分是很多人一开始会问的:

“DynamicJsonDocument(256) 这个 256 要怎么选?”

简单记:

  • JSON 越复杂(对象层级越多、字符串越长),需要越大

  • 官方有一个在线助手可以估算(ArduinoJson Assistant),你可以稍后再看

  • 自己测试时,可以先给大一点,比如 512 / 1024,看内存够用就行

如果 doc 太小,deserializeJson() 会失败,错误类似:

InvalidInput: NoMemory

你只要把文档大小调大一点就好。


八、这一节你要掌握到什么程度?

对于你现在这个阶段,我建议的“达标标准”是:

  1. 知道 JSON 是“键值 + 嵌套对象 + 数组”的文本格式

  2. 会看懂以下这条逻辑链:

    String payload = http.getString();
    DynamicJsonDocument doc(512);
    deserializeJson(doc, payload);
    float temp = doc["temp"].as();
  3. 对“嵌套访问”和“数组访问”有概念:

    • doc["sensor"]["temp"]

    • doc["tags"][0]

  4. 知道解析失败要先看 deserializeJson() 的返回错误,而不是一顿胡乱打印。

掌握了这些,你以后用什么云平台(OneNet、自己搭的后端、各种 HTTP API),只要它返回 JSON,你都能在 ESP32 里“吃进来 → 拆出来 → 用”。

posted @ 2026-01-04 12:50  yangykaifa  阅读(93)  评论(0)    收藏  举报