ArduinoJson如何将名称匹配的JSON对象所有成员复制到二维数组
实现方案
1 先修正结构体定义
你当前的struct cocktailData定义存在容量不足的问题:char portions[7][1]每个元素仅分配1字节存储空间,既无法存储长度超过1的原料名字符串,也无法存储大于127的数值,需要先调整为符合存储需求的结构:
struct cocktailData { char name[25]; // 存储选中的鸡尾酒名称 // 按你的需求,最多支持7种原料(你描述的8种对应索引0-7的话可以把7改成8) char portions[8][2][25]; // 第一维:原料索引0-7;第二维:0存原料名、1存用量的字符串形式;第三维:单条内容最长24字符 uint8_t ingredientCnt; // 实际读取到的原料数量,方便后续调用 } data;
如果不需要把用量存为字符串,也可以拆分为两个独立数组,内存占用更低、调用更方便:
struct cocktailData { char name[25]; char ingredientNames[8][25]; // 存储原料名 int portionValues[8]; // 存储数值型用量乘数 uint8_t ingredientCnt; } data;
以下示例代码采用拆分数组的结构,内存占用更低、调用更方便
2 前置准备
- 确保你的JSON文件格式正确:你提供的示例中
Americano的三个键值对之间缺少英文逗号,会导致解析失败,修正后的正确片段:
"Americano": { "campari": 1, "red vermouth": 2, "soda": 3 },
- 安装ArduinoJson V6版本库(Arduino库管理器可直接搜索安装)
3 完整实现代码
#include <SPI.h> #include <SD.h> #include <ArduinoJson.h> // 定义结构体 struct cocktailData { char name[25]; char ingredientNames[8][25]; int portionValues[8]; uint8_t ingredientCnt; } data; // SD卡CS引脚,根据自己的板子调整,UNO默认是D10 const int SD_CS_PIN = 10; void setup() { Serial.begin(115200); // 初始化SD卡 if (!SD.begin(SD_CS_PIN)) { Serial.println("SD卡初始化失败"); return; } // 调用读取配方函数,目标鸡尾酒为Americano if (loadCocktailRecipe("Americano")) { Serial.print("成功读取配方:"); Serial.println(data.name); Serial.print("原料数量:"); Serial.println(data.ingredientCnt); // 遍历打印所有原料和用量 for (int i=0; i<data.ingredientCnt; i++) { Serial.print(data.ingredientNames[i]); Serial.print(" : "); Serial.println(data.portionValues[i]); } } else { Serial.println("读取配方失败"); } } void loop() { // 你的业务逻辑 } // 加载指定名称的鸡尾酒配方,成功返回true,失败返回false bool loadCocktailRecipe(const char* targetCocktailName) { // 打开JSON文件 File jsonFile = SD.open("/cocktails.json"); if (!jsonFile) { Serial.println("无法打开cocktails.json文件"); return false; } // 分配JSON解析缓冲区,大小根据你的JSON总大小调整,这里给2KB足够存示例内容 DynamicJsonDocument doc(2048); DeserializationError error = deserializeJson(doc, jsonFile); // 用完就关闭文件,节省内存 jsonFile.close(); if (error) { Serial.print("JSON解析失败:"); Serial.println(error.f_str()); return false; } // 获取Cocktails对象 JsonObject cocktailsObj = doc["Cocktails"]; // 检查目标鸡尾酒是否存在 if (!cocktailsObj.containsKey(targetCocktailName)) { Serial.print("未找到指定鸡尾酒:"); Serial.println(targetCocktailName); return false; } // 先清空原有结构体数据 memset(&data, 0, sizeof(data)); // 保存鸡尾酒名称 strncpy(data.name, targetCocktailName, sizeof(data.name)-1); // 获取目标鸡尾酒的配方对象 JsonObject recipeObj = cocktailsObj[targetCocktailName]; uint8_t index = 0; // 遍历配方的所有键值对 for (JsonPair kv : recipeObj) { // 最多支持8种原料,超出就停止遍历 if (index >= 8) break; // 拷贝原料名 strncpy(data.ingredientNames[index], kv.key().c_str(), sizeof(data.ingredientNames[index])-1); // 保存用量 data.portionValues[index] = kv.value().as<int>(); index++; } // 保存实际原料数量 data.ingredientCnt = index; return true; }
4 代码说明
- 如果你坚持要用二维数组存储
portions,只需要在遍历的时候把原料名存到data.portions[index][0],把用量转为字符串后存到data.portions[index][1]即可 - 如果你需要支持更多配方,可以调大
DynamicJsonDocument的容量,ArduinoJson官网提供了容量计算器可以直接计算所需大小 - 代码中所有字符串拷贝都预留了结束符位置,避免内存溢出
内容的提问来源于stack exchange,提问作者Jeebus
相关产品推荐
相关产品推荐

