玩轉ArduinoJson庫 V5版本

文章目錄

1.前言

    一直以來,博主的事例代碼中都一直使用到JSON數據格式。而很多初學者一直對JSON格式有很大疑惑,所以博主特意分出一篇博文來重點講解Arduino平臺下的JSON庫——ArduinoJSON。
    讀者需要注意一下幾點:

  • ArduinoJSON的Github地址請參考 wiki
  • ArduinoJSON說明文檔可以參考 wiki,博主強烈建議讀者可以嘗試去閱讀這些文檔,肯定獲益匪淺。
  • ArduinoJSON目前分爲兩個大版本 V5 和 V6版本(估計很多初學者會經常看到Arduino IDE提示自己升級ArduinoJSON庫版本),博主也會分別講解兩個版本。
  • 下載完庫之後,只需要包含以下頭文件即可使用
#include <ArduinoJson.h>

    廢話少說,請讀者認真以下乾貨內容。

2.JSON基礎介紹

2.1 什麼是Json?

  • Json 指的是JavaScript對象表示法(JavaScript Object Notation)。
  • Json 是輕量級的文本數據交換格式,類似於XML,比XML更小、更快,更容易解析。
  • Json 獨立於語言。
  • Json 具有自我描述性,容易理解。

2.2 具體使用例子

2.2.1 JSON對象例子

JSON對象描述

{
    "motor": {
        "left": 100,
        "right": 20
    },
    "servo": {
        "servo_1": 90
    }
}

JSON對象語法說明

  • 這裏分別有兩個對象(對象是用{}區分),對象分別是motor、servo。
  • 對於對象motor來說,有兩個字段 left 和 right,它們的值類型是int型,值分別是100和20;
  • 對於對象servo來說,有一個字段servo_1,它的值類型是int型,值是90。

2.2.2 JSON對象數組

JSON數組描述

{
    "employees": [
        {
            "firstName": "Bill",
            "lastName": "Gates"
        },
        {
            "firstName": "George",
            "lastName": "Bush"
        },
        {
            "firstName": "Thomas",
            "lastName": "Carter"
        }
    ]
}

JSON數組語法說明

  • 這裏有一個對象數組(數組用[]區分)employees。
  • 每個對象裏面有兩個字段firstName和lastName,它們的值類型都是String。

重點

  • 無論是JSON對象還是JSON數組,它們的核心都是鍵值對,也就是key-value形式。

3.ArduinoJson庫

    上面說到,ArduinoJson庫有兩個大版本 —— V5 和 V6。
    不管是V5還是V6版本,在編碼之前,請到Arduino IDE的管理庫去下載:
image

    請讀者按照自己的需求分別去下載不同版本(當然博主建議用新不用舊,畢竟一般新版本都是經過舊版本升級優化)。
    不管是V5 還是 V6版本,對於開發者來說有三個概念是共通的:

  • JsonObject —— json對象(存儲key-value鍵值對的集合,每一個key是一個字符串,每一個value是一個JsonVariant)
  • JsonArray —— json對象數組(存儲一個JsonVariant的數組)
  • JsonVariant —— json變體(存儲可以放在json文件中的任意類型數據,包括int,float,數組,對象,開發者可以認爲它是一個抽象的對象概念,底層內容,讀者理解即可)

    除了理解上面的三個概念,博主認爲讀者還必須注意以下兩點:

  • 構造json數據,一定是從最外層開始構造,從外到裏一層層構造
  • 解析json數據,雖然是交給arduinojson庫處理,但是我們獲取數據也是從最外層開始獲取,從外到裏一層層獲取

    記住上面的黑體內容,接下來,我們針對不同版本的ArduinoJson庫來講解使用以及注意事項。

3.1 ArduinoJson V5版本

    假設讀者下載的V5版本的,那麼可以參考 這裏 的API說明。讀者需要注意一下jsonBuffer,因爲V5版本的json操作都是在它上面。
    對於開發者來說,使用JSON無非就是編碼或者解碼,所以博主也會分出兩種情況來講解。
    首先我們來看看V5版本常用的百度腦圖:

image

    可以看出,方法主要分爲三大類:

  • JsonBuffer相關,這是整個json庫的入口,它負責高效管理內存以及調用json解析器;
  • JsonObject相關;
  • JsonArray相關;

3.1.1 JsonBuffer

    JsonBuffer作爲整個V5版本ArduinoJson庫的入口,負責處理整個json數據的內存管理以及構造解析工作,這是我們需要首先重點關注的內容。
    它包括兩個實現類:

  • DynamicJsonBuffer,內存分配在heap區,無固定大小,可以自動增長所需空間,方法調用完自動回收;
  • StaticJsonBuffer,內存分配在stack區,有固定大小,大小值由開發者定義,方法調用完自動回收;

    對於一些內存使用比較緊缺的機器,博主建議儘量用固定大小的StaticJsonBuffer(這個需要提前預知內容的大小,而這個內容大小可以通過計算得來,請點 wiki 參考)。

    那麼,我們開發者可能會關心以下幾個常見問題:

1. 問題1,如何去確定buffer的大小?

  • 假設整個json數據結構是固定的,開發者可以使用StaticJsonBuffer。舉個例子:
{"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}

    那麼通過JSON_OBJECT_SIZE(n)和JSON_ARRAY_SIZE(n)可以計算出內存大小:

const int BUFFER_SIZE = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2);
StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;

    當然我們可以藉助工具來幫我們計算內存大小,具體可以參考 ArduinoJson Assistant

  • 假設整個json數據結構是非固定的,開發者可以使用DynamicJsonBuffer,可以動態增長(動態增長就意味着可能會內存溢出,整個是需要開發者去考慮的),當然儘量給DynamicJsonBuffer一個足夠大的初始值,可以減少調用malloc()方法去分配內存。

  • json解析器根據輸入的內容會有不同的策略字符串。如果輸入的內容是可寫內容,比如 char* 或者 char[],解析器不會申請額外的內存空間,也就是“zero-copy”模式。如果輸入的內容是自讀內容,比如 const char* 或者 字符串 或者steam流,解析器會重新拷貝一份該字符串作爲可寫操作,這樣就意味着JsonBuffer大小可能和我們預想的不一樣。所以,博主建議開發者儘量是char* 或者 char[]。

2. StaticJsonBuffer 和 DynamicJsonBuffer的區別?

image

3. 如何去重用一個JsonBuffer?

  • 要重用JsonBuffer,開發者的版本必須大於5.11.0,並且使用clear方法去重用,當然json庫的開發者建議不要去重用jsonbuffer,容易出問題。直接看個例子:
// STEP1: parse input
StaticJsonBuffer<200> jsonBuffer;
JsonObject& inputObject = jsonBuffer.parseObject(inputJson);

...etc...

// STEP2: generate output
jsonBuffer.clear();
JsonObject& outputObject = jsonBuffer.createObject();
outputObject["hello"] = inputObject["world"];

    看起來沒有什麼問題,但實際上有訪問錯誤,請看分析:

  1. inputObject是一個json對象的指針,由JsonBuffer管理內存;
  2. clear方法重置了jsonbuffer內存池,運行重新使用inputObject的內存空間;
  3. outputObject對象被創建,其地址和inputObject一樣;
  4. 到這一步大家應該知道會發生什麼問題了吧,inputObject變成了一個危險指針,其內容是無法預知的;

    那麼要怎麼解決這個問題呢?有兩種解決方案:

  • 使用更大的JsonBuffer
// STEP1: parse input
StaticJsonBuffer<400> jsonBuffer;
JsonObject& inputObject = jsonBuffer.parseObject(inputJson);

...etc...

// STEP2: generate output
JsonObject& outputObject = jsonBuffer.createObject();
outputObject["hello"] = inputObject["world"];
  • 使用第二個JsonBuffer:
// STEP1: parse input
StaticJsonBuffer<200> jsonBuffer1;
JsonObject& inputObject = jsonBuffer1.parseObject(inputJson);

...etc...

// STEP2: generate output
StaticJsonBuffer<200> jsonBuffer2;
JsonObject& outputObject = jsonBuffer2.createObject();
outputObject["hello"] = inputObject["world"];

4. 爲什麼不要去用一個全局的JsonBuffer?

  • 具體可以參考 wiki
  • 博主認爲最重要的原因還是一般我們用完json就應該釋放掉它,全局jsonbuffer會一直佔用着內存空間。

    接下來,看看JsonBuffer的一些常用方法:

3.1.1.1 clear —— 重置內存指針,複用內存空間,慎用

函數說明

/**
 * 重置內存指針,複用內存空間,慎用
 */
void clear();

例子說明

StaticJsonBuffer<200> jb;

JsonObject& obj1 = jb.parseObject(json1);
// we can use obj1 here...

jb.clear();
// now obj1 is dangling!!!

// ...but we can reuse the JsonBuffer
JsonObject& obj2 = jb.parseObject(json2);

注意

  • 重要說三遍,慎用,慎用,慎用;
  • 一旦你調用 JsonBuffer::clear(),所有之前分配的jsonobject或者jsonbuffer都會變成無效;

3.1.1.2 createArray —— 創建空json數組,併爲它分配內存空間

函數說明

/**
 * 創建空json數組,併爲它分配內存空間
 * @return json數組地址
 * @Note 如果分配失敗,會提示分配失敗
 */
JsonArray& createArray();

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
array.add("hello");
array.add("world");

3.1.1.3 createObject —— 創建空json對象,併爲它分配內存空間

函數說明

/**
 * 創建空json對象,併爲它分配內存空間
 * @return json對象地址
 * @Note 如果分配失敗,會提示分配失敗
 */
JsonObject createObject();

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
object["hello"] = "world";

3.1.1.4 parse —— 解析json(數組或者對象)字符串

函數說明

/**
 * 解析json(數組或者對象)字符串
 * @param json json字符串
 * @param nestingLimit json深度限制
 * @return jsonVariant對象
 * @Note 此方法用於不可預知json格式的前提下使用的
 */
// The first overload, which accepts a modifiable array of chars, is the most efficient
// since it allows the zero-copy feature.
JsonVariant parse(char* json, uint8_t nestingLimit=10);

// The following overloads, which accept read-only strings, require a bigger JsonBuffer
// because parts of the JSON input has to be copied.
JsonVariant parse(const char* json, uint8_t nestingLimit=10);
JsonVariant parse(const String& json, uint8_t nestingLimit=10);
JsonVariant parse(const std::string& json, uint8_t nestingLimit=10);
JsonVariant parse(const __FlashStringHelper* json, uint8_t nestingLimit=10);

// The two last overloads, which accept input streams, make copy of the input too.
JsonVariant parse(Stream& json, uint8_t nestingLimit=10);
JsonVariant parse(std::istream& json, uint8_t nestingLimit=10);

例子說明

char json[] = "[\"hello\",\"world\"]";

StaticJsonBuffer<200> jsonBuffer;
JsonVariant variant = jsonBuffer.parse(json);

if (variant.is<JsonArray>()) // true in this example
{
    JsonArray& array = variant;
    const char* hello = array[0];
    const char* world = array[1];

}

3.1.1.5 parseArray —— 解析json數組字符串

函數說明

/**
 * 解析json數組字符串
 * @param json json字符串
 * @param nestingLimit json深度限制
 * @return jsonArray對象
 * @Note 此方法用於預知json格式是jsonArray的前提下使用的
 */
// The first overload, which accepts a modifiable array of chars, is the most efficient
// since it allows the zero-copy feature.
JsonArray& parseArray(char* json, uint8_t nestingLimit=10);

// The following overloads, which accept read-only strings, require a bigger JsonBuffer
// because parts of the JSON input has to be copied.
JsonArray& parseArray(const char* json, uint8_t nestingLimit=10);
JsonArray& parseArray(const String& json, uint8_t nestingLimit=10);
JsonArray& parseArray(const std::string& json, uint8_t nestingLimit=10);
JsonArray& parseArray(const __FlashStringHelper* json, uint8_t nestingLimit=10);

// The two last overloads, which accept input streams, make copy of the input too.
JsonArray& parseArray(Stream& json, uint8_t nestingLimit=10);
JsonArray& parseArray(std::istream& json, uint8_t nestingLimit=10);

例子說明

char json[] = "[\"hello\",\"world\"]";
StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.parseArray(json);
const char* hello = array[0];
const char* world = array[1];

3.1.1.6 parseObject —— 解析json對象字符串

函數說明

/**
 * 解析json對象字符串
 * @param json json字符串
 * @param nestingLimit json深度限制
 * @return json對象
 * @Note 此方法用於預知json格式是jsonObject的前提下使用的
 */
// The first overload, which accepts a modifiable array of chars, is the most efficient
// since it allows the zero-copy feature.
JsonObject& parseObject(char* json, uint8_t nestingLimit=10);

// The following overloads, which accept read-only strings, require a bigger JsonBuffer
// because parts of the JSON input has to be copied.
JsonObject& parseObject(const char* json, uint8_t nestingLimit=10);
JsonObject& parseObject(const String& json, uint8_t nestingLimit=10);
JsonObject& parseObject(const std::string& json, uint8_t nestingLimit=10);
JsonObject& parseObject(const __FlashStringHelper* json, uint8_t nestingLimit=10);

// The two last overloads, which accept input streams, make copy of the input too.
JsonObject& parseObject(Stream& json, uint8_t nestingLimit=10);
JsonObject& parseObject(std::istream& json, uint8_t nestingLimit=10);

例子說明

char json[] = "{\"hello\":\"world\"}";
StaticJsonBuffer<200> jsonBuffer;
JsonObject& object = jsonBuffer.parseObject(json);
const char* world = object["hello"];

3.1.1.7 size —— JsonBuffer當前已用大小

函數說明

/**
 * JsonBuffer當前已用大小
 * @return 當前已用大小
 */
size_t size() const;

例子說明

StaticJsonBuffer<200> jsonBuffer;
Serial.println(jsonBuffer.size());
jsonBuffer.createObject();
Serial.println(jsonBuffer.size());
jsonBuffer.createArray();
Serial.println(jsonBuffer.size());

在8位單片機中會打印:

0
4
8

3.1.2 JsonObject

    在JsonBuffer所構造出來的內存空間中,Json對象的入口就是JsonObject。
    讓我們看看它的常用操作方法:

3.1.2.1 begin / end —— 返回一個迭代器,可用於對象中的所有鍵值對

函數說明

/**
 * 返回一個迭代器,可用於對象中的所有鍵值對
 * @return iterator  iterator包括key和value
 */
JsonObject::iterator begin();
JsonObject::iterator end();
JsonObject::const_iterator begin() const;
JsonObject::const_iterator end() const;

例子說明

char json[] = "{\"first\":\"hello\",\"second\":\"world\"}";
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(json);

// using C++11 syntax (preferred):
for (auto kv : root) {
    Serial.println(kv.key);
    Serial.println(kv.value.as<char*>());
}

// using C++98 syntax (for older compilers):
for (JsonObject::iterator it=root.begin(); it!=root.end(); ++it) {
    Serial.println(it->key);
    Serial.println(it->value.as<char*>());
}

測試結果:

first
hello
second
world

3.1.2.2 containsKey —— 判斷對象是否包含某一個key

函數說明

/**
 * 判斷對象是否包含某一個key
 * @param key key名字
 * @return bool
 */
bool containsKey(const char* key) const;
bool containsKey(const String& key) const;
bool containsKey(const std::string& key) const;
bool containsKey(const __FlashStringHelper& key) const;

例子說明

StaticJsonBuffer<256> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["city"] = "Paris";

bool hasCity = root.containsKey("city"); // true
bool hasCountry = root.containsKey("country"); // false

注意:

  • json庫的開發者不建議使用該方法,因爲就算沒有值也會返回一個空值。庫開發者給了例子建議:
if (root.containsKey("error"))
{
  const char* error = root["error"]
  Serial.println(error);
  return;
}

可以改成:

JsonVariant error = root["error"];
if (error.success()) 
{
  Serial.println(error.as<char*>());
  return;
}

或者更加簡單快速的方法:

const char* error = root["error"];
if (error) 
{
  Serial.println(error);
  return;
}

3.1.2.3 createNestedArray —— 在當前對象中添加子key,子value爲json數組

函數說明

/**
 * 在當前對象中添加子key,子value爲json數組
 * @param key key名字
 * @return JsonArray
 */
JsonArray& createNestedArray(const char* key) const;
JsonArray& createNestedArray(char* key) const; // see Remarks
JsonArray& createNestedArray(const String& key) const; // see Remarks
JsonArray& createNestedArray(const std::string& key) const; // see Remarks
JsonArray& createNestedArray(const __FlashStringHelper* key) const; // see Remarks

例子說明

StaticJsonBuffer<256> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["status"] = "on";
JsonArray& levels = root.createNestedArray("levels");
levels.add(10);
levels.add(30);
root.prettyPrintTo(Serial);

打印結果:

{
  "status": "on",
  "levels": [
    10,
    20
  ]
}

3.1.2.4 createNestedObject —— 在當前對象中添加子key,子value爲json對象

函數說明

/**
 * 在當前對象中添加子key,子value爲json對象
 * @param key key名字
 * @return JsonObject對象
 */
JsonObject& createNestedObject(const char* key) const;
JsonObject& createNestedObject(char* key) const; // see Remarks
JsonObject& createNestedObject(const String& key) const; // see Remarks
JsonObject& createNestedObject(const std::string& key) const; // see Remarks
JsonObject& createNestedObject(const __FlashStringHelper* key) const; // see Remarks

例子說明

StaticJsonBuffer<256> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["city"] = "Paris";
JsonObject& weather = root.createNestedObject("weather");
weather["temp"] = 14.2;
weather["cond"] = "cloudy";
root.prettyPrintTo(Serial);

打印結果:

{
  "city": "Paris",
  "weather": {
    "temp": 14.20,
    "cond": "cloudy"
  }
}

3.1.2.5 get —— 獲取某一個key的值,T表示值類型

函數說明

/**
 * 獲取某一個key的值,T表示值類型
 * @param key key名字
 * @return 值
 */
bool            get<bool>           (TString key) const;
const char*     get<const char*>    (TString key) const;
double          get<double>         (TString key) const;
float           get<float>          (TString key) const;
JsonVariant     get<JsonVariant>    (TString key) const;
signed char     get<signed char>    (TString key) const;
signed int      get<signed int>     (TString key) const;
signed long     get<signed long>    (TString key) const;
signed short    get<signed short>   (TString key) const;
std::string     get<std::string>    (TString key) const;
String          get<String>         (TString key) const;
unsigned char   get<unsigned char>  (TString key) const;
unsigned int    get<unsigned int>   (TString key) const;
unsigned long   get<unsigned long>  (TString key) const;
unsigned short  get<unsigned short> (TString key) const;

例子說明

char json[] = "{\"pi\":3.14}";
StaticJsonBuffer<256> jsonBuffer;
JsonObject& object = jsonBuffer.parseObject(json);
float pi = object.get<float>("pi"); // template version of get()
const char* value2 = object.get<const char*>("toto"); // returns NULL

3.1.2.6 is —— 判斷某一個key的值是否是T類型

函數說明

/**
 * 判斷某一個key的值是否是T類型
 * @param key key名字
 * @return bool
 *         true 爲T類型
 *         false 不是T類型
 */
bool is<bool>              (TString key) const;

bool is<const char*>       (TString key) const;
bool is<char*>             (TString key) const;

bool is<double>            (TString key) const;
bool is<float>             (TString key) const;

bool is<signed char>       (TString key) const;
bool is<signed int>        (TString key) const;
bool is<signed long>       (TString key) const;
bool is<signed short>      (TString key) const;
bool is<unsigned char>     (TString key) const;
bool is<unsigned int>      (TString key) const;
bool is<unsigned long>     (TString key) const;
bool is<unsigned short>    (TString key) const;
bool is<signed long long>  (TString key) const;   // <- may require ARDUINOJSON_USE_LONG_LONG
bool is<unsigned long long>(TString key) const;   // <- may require ARDUINOJSON_USE_LONG_LONG

bool is<JsonArray>         (TString key) const;
bool is<JsonObject>        (TString key) const;

例子說明

char json[] = "{\"name\":\"toto\",\"pi\":3.14}";
StaticJsonBuffer<256> jsonBuffer;
JsonObject& obj = jsonBuffer.parseObject(json);

bool nameIsString = obj.is<char*>("name"); // <- true
bool piIsFloat = obj.is<float>("pi"); // <- true

// but we could also use JsonVariant.is<T>(), like that:
nameIsString = obj["name"].is<char*>(); // <- true
piIsFloat = obj["pi"].is<float>(); // <- true

3.1.2.7 measureLength —— 計算當前對象通過printTo打印出來的長度

函數說明

/**
 * 計算當前對象通過printTo打印出來的長度
 * @return size_t 長度值
 * @Note 跟 JsonObject::printTo() 關聯,此方法通常用於http協議中的 Content-Length頭
 */
size_t measureLength() const

例子說明

// Send headers
client.println("Content-Type: application/json");
client.print("Content-Length: ");
client.println(root.measureLength());

// Terminate headers
client.println();

// Send body
root.printTo(client);

3.1.2.8 measurePrettyLength —— 計算當前對象通過prettyPrintTo打印出來的長度

函數說明

/**
 * 計算當前對象通過prettyPrintTo打印出來的長度
 * @return size_t 長度值
 * @Note 跟 JsonObject::prettyPrintTo() 關聯,此方法通常用於http協議中的 Content-Length頭
 */
size_t measurePrettyLength() const

例子說明

// Send headers
client.println("Content-Type: application/json");
client.print("Content-Length: ");
client.println(root.measurePrettyLength());

// Terminate headers
client.println();

// Send body
root.prettyPrintTo(client);

3.1.2.9 prettyPrintTo —— 格式化輸出json字符串

函數說明

/**
 * 格式化輸出json字符串
 * @param buffer 內容輸出到內存區
 * @param size 內存區的大小
 * @param Print 打印流 比如 Serial
 * @param String 打印到字符串
 * @return 返回已寫大小
 */
size_t prettyPrintTo(char* buffer, size_t size) const;
size_t prettyPrintTo(char buffer[size]) const;
size_t prettyPrintTo(Print &) const;
size_t prettyPrintTo(String &) const;
size_t prettyPrintTo(std::string &) const;

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
object["hello"] = "world";
object.prettyPrintTo(Serial);

打印結果:

{
  "hello": "world"
}

3.1.2.10 printTo —— 壓縮式輸出json字符串

函數說明

/**
 * 壓縮式輸出json字符串
 * @param buffer 內容輸出到內存區
 * @param size 內存區的大小
 * @param Print 打印流 比如 Serial
 * @param String 打印到字符串
 * @return 返回已寫大小
 */
size_t printTo(char* buffer, size_t size) const;
size_t printTo(char buffer[size]) const;
size_t printTo(Print &) const;
size_t printTo(String &) const;
size_t printTo(std::string &) const;

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
object["hello"] = "world";
object.printTo(Serial);

打印結果:

{"hello":"world"}

注意

  • 該方法屬於壓縮式輸出,可以節省空間;

3.1.2.11 remove —— 移除特定key和value

函數說明

/**
 * 移除特定key和value
 * @param key key名
 */
void remove(const char* key);
void remove(const String& key);
void remove(const std::string& key);
void remove(const __FlashStringHelper* key);

例子說明

JsonObject& object = jsonBuffer.createObject();
object["A"] = 1;
object["B"] = 2;
object["C"] = 3;
object.remove("B");
object.printTo(Serial);

打印結果:

{"A":1,"C":3}

注意

  • 該方法只會移除key-value,但是並不會釋放key-value對應的jsonbuffer空間,也不建議在循環中同時add和remove key-value;

3.1.2.12 set —— 設置特定key的值

函數說明

/**
 * 設置特定key的值
 * @param key key名
 * @param value 值
 * @return bool 是否設置成功
 */
bool set(TString key, bool value);
bool set(TString key, float value);
bool set(TString key, double value);
bool set(TString key, signed char value);
bool set(TString key, signed long value);
bool set(TString key, signed int value);
bool set(TString key, signed short value);
bool set(TString key, unsigned char value);
bool set(TString key, unsigned long value);
bool set(TString key, unsigned int value);
bool set(TString key, unsigned short value);
bool set(TString key, const char *value);
bool set(TString key, char *value); // see Remarks
bool set(TString key, const String &value); // see Remarks
bool set(TString key, const std::string &value); // see Remarks
bool set(TString key, const __FlashStringHelper* value); // see Remarks
bool set(TString key, JsonArray &array);
bool set(TString key, JsonObject &object);
bool set(TString key, const JsonVariant &value);

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
object.set("hello","world");
object.printTo(Serial);

打印結果:

{"hello":"world"}

3.1.2.13 size —— 返回對象鍵值對的個數

函數說明

/**
 * 返回對象鍵值對的個數
 * @return size_t 個數
 */
size_t size() const;

例子說明

JsonObject& object = jsonBuffer.createObject();
object["hello"] = "world";
Serial.println(object.size()); // 1

3.1.2.14 operator[] —— get/set的快捷方式

函數說明

/**
 * get/set的快捷方式
 */
JsonVariant& operator[](const char* key);
JsonVariant& operator[](char* key); // see Remarks
JsonVariant& operator[](const String& key); // see Remarks
JsonVariant& operator[](const std::string& key); // see Remarks
JsonVariant& operator[](const __FlashStringHelper* key); // see Remarks

const JsonVariant& operator[](const char* key) const;
const JsonVariant& operator[](const String& key) const;
const JsonVariant& operator[](const std::string& key) const;
const JsonVariant& operator[](const __FlashStringHelper* key) const;

例子說明

JsonObject& object = jsonBuffer.createObject();
object["hello"] = "world";
const char* world = object["hello"];

3.1.2.15 success —— 判斷對象是否是有效

函數說明

/**
 * 判斷對象是否是有效(解析或者分配內存)
 * @return bool
 */
bool success() const;

例子說明

//Example 1: parsing success:
StaticJsonBuffer<200> jsonBuffer;
JsonObject& object = jsonBuffer.parseObject("{\"hello\":\"world\"}");
Serial.println(object.success()); // true

//Example 2: parsing failure:
StaticJsonBuffer<200> jsonBuffer;
JsonObject& object = jsonBuffer.parseObject("[\"hello\",\"world\"]");
Serial.println(object.success()); // false

//Example 3: allocation success:
StaticJsonBuffer<200> jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
Serial.println(object.success()); // true

//Example 4: allocation failure:
StaticJsonBuffer<1> jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
Serial.println(object.success()); // false

3.1.3 JsonArray

    在JsonBuffer所構造出來的內存空間中,Json數組的入口就是JsonArray。
    讓我們看看它的常用操作方法:

3.1.3.1 add —— 往數組中加入value

函數說明

/**
 * 往數組中加入value
 * @param value 值
 * @return bool 是否添加成功,如果返回false一般都是jsonbuffer沒有足夠的空間
 */
bool add(bool value);
bool add(float value);
bool add(double value);
bool add(signed char value);
bool add(signed long value);
bool add(signed int value);
bool add(signed short value);
bool add(unsigned char value);
bool add(unsigned long value);
bool add(unsigned int value);
bool add(unsigned short value);
bool add(const char *value);
bool add(char *value); // see Remarks
bool add(const String &value); // see Remarks
bool add(const std::string &value); // see Remarks
bool add(const __FlashStringHelper *value); // see Remarks
bool add(JsonArray &array);
bool add(JsonObject &object);
bool add(const JsonVariant &variant);

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
array.add("hello");
array.add(3.14156);
array.printTo(Serial);

打印結果:

["hello",3.14156]

3.1.3.2 begin/end —— 返回一個迭代器,可用於數組中的所有對象

函數說明

/**
 * 返回一個迭代器,可用於數組中的所有對象
 * @return iterator
 */
JsonObject::iterator begin();
JsonObject::iterator end();
JsonObject::const_iterator begin() const;
JsonObject::const_iterator end() const;

例子說明

char json[] = "[\"one\",\"two\",\"three\"]";
DynamicJsonBuffer jsonBuffer;
JsonArray& arr = jsonBuffer.parseArray(json);

// using C++11 syntax (preferred):
for (auto value : arr) {
    Serial.println(value.as<char*>());
}

// using C++98 syntax (for older compilers):
for (JsonArray::iterator it=arr.begin(); it!=arr.end(); ++it) {
    Serial.println(it->as<char*>());
}

打印結果:

one
two
three

3.1.3.3 copyFrom —— 把c數組轉成json數組

函數說明

/**
 * 把c數組轉成json數組
 * @param array 數組
 * @param len 數組大小
 */
// 1D arrays
JsonArray::copyFrom(T array[len]);
JsonArray::copyFrom(T* array, size_t len);

// 2D arrays
JsonArray::copyFrom(T array[][]);

例子說明

int values[] = {1, 2, 3};

StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
array.copyFrom(values);
array.printTo(Serial);

打印結果:

[1,2,3]

3.1.3.4 copyTo —— 把json數組轉成c數組

函數說明

/**
 * 把json數組轉成c數組
 * @param array 數組
 */
JsonArray::copyTo(int array[]);
JsonArray::copyTo(double array[]);
JsonArray::copyTo(const char* array[]);

例子說明

int values[3];

StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.parseArray("[1,2,3]");
array.copyTo(values);//現在values變成1,2,3

3.1.3.5 createNestedArray —— 添加json數組

函數說明

/**
 * 添加json數組
 * @return  JsonArray json數組
 */
JsonArray& createNestedArray();

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
array.add("hello");
JsonArray& nested = array.createNestedArray();
nested.add("world");
array.printTo(Serial);

打印結果:

["hello",["world"]]

3.1.3.6 createNestedObject —— 添加json對象

函數說明

/**
 * 添加json對象
 * @return  JsonObject json對象
 */
JsonObject& createNestedObject();

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
JsonObject& nested = array.createNestedObject();
nested["hello"] = "world";
array.printTo(Serial);

打印結果:

[{"hello":"world"}]

3.1.3.7 get —— 獲取具體index的值

函數說明

/**
 * 獲取具體index的值
 * @param  index 索引
 * @return  T 返回索引對應的值
 */
bool            get<bool>           (size_t index) const;
const char*     get<const char*>    (size_t index) const;
const char*     get<char*>          (size_t index) const;
double          get<double>         (size_t index) const;
float           get<float>          (size_t index) const;
signed char     get<signed char>    (size_t index) const;
signed int      get<signed int>     (size_t index) const;
signed long     get<signed long>    (size_t index) const;
signed short    get<signed short>   (size_t index) const;
unsigned char   get<unsigned char>  (size_t index) const;
unsigned int    get<unsigned int>   (size_t index) const;
unsigned long   get<unsigned long>  (size_t index) const;
unsigned short  get<unsigned short> (size_t index) const;
JsonVariant     get<JsonVariant>    (size_t index) const;
std::string     get<std::string>    (size_t index) const;
String          get<String>         (size_t index) const;

例子說明

char json[] = "[1,3.14]";
StaticJsonBuffer<256> jsonBuffer;
JsonArray& array = jsonBuffer.parseArray(json);
int value0 = array.get(0); // implicit cast of the JsonVariant
float value1 = array.get<float>(1); // template version of get()
const char* value2 = array.get(2); // returns NULL

注意

  • integer默認值是0
  • double默認值是0.0
  • char* 默認是是NULL

3.1.3.8 is —— 判斷具體index的值是否爲T類型

函數說明

/**
 * 判斷具體index的值是否爲T類型
 * @return  bool 是否是目標類型
 */
bool is<bool>              (size_t index) const;

bool is<const char*>       (size_t index) const;
bool is<char*>             (size_t index) const;

bool is<double>            (size_t index) const;
bool is<float>             (size_t index) const;

bool is<signed char>       (size_t index) const;
bool is<signed int>        (size_t index) const;
bool is<signed long>       (size_t index) const;
bool is<signed short>      (size_t index) const;
bool is<unsigned char>     (size_t index) const;
bool is<unsigned int>      (size_t index) const;
bool is<unsigned long>     (size_t index) const;
bool is<unsigned short>    (size_t index) const;
bool is<signed long long>  (size_t index) const;   // <- may require ARDUINOJSON_USE_LONG_LONG
bool is<unsigned long long>(size_t index) const;   // <- may require ARDUINOJSON_USE_LONG_LONG

bool is<JsonArray>         (size_t index) const;
bool is<JsonObject>        (size_t index) const;

例子說明

char json[] = "[\"pi\",3.14]";
StaticJsonBuffer<256> jsonBuffer;
JsonArray& array = jsonBuffer.parseArray(json);

bool firstIsString = array.is<char*>(0); // <- true
bool secondIsFloat = array.is<float>(1); // <- true

// but we could also use JsonVariant.is<T>(), like that:
firstIsString = array[0].is<char*>(); // <- true
secondIsFloat = array[1].is<float>(); // <- true

3.1.3.9 measureLength —— 計算當前json數組通過printTo打印出來的長度

函數說明

/**
 * 計算當前json數組通過printTo打印出來的長度
 * @return size_t 長度值
 * @Note 跟 JsonArray::printTo() 關聯,此方法通常用於http協議中的 Content-Length頭
 */
size_t measureLength() const

例子說明

// Send headers
client.println("Content-Type: application/json");
client.print("Content-Length: ");
client.println(root.measureLength());

// Terminate headers
client.println();

// Send body
root.printTo(client);

3.1.3.10 measurePrettyLength —— 計算當前json數組通過prettyPrintTo打印出來的長度

函數說明

/**
 * 計算當前json數組通過prettyPrintTo打印出來的長度
 * @return size_t 長度值
 * @Note 跟 JsonArray::prettyPrintTo() 關聯,此方法通常用於http協議中的 Content-Length頭
 */
size_t measurePrettyLength() const

例子說明

// Send headers
client.println("Content-Type: application/json");
client.print("Content-Length: ");
client.println(root.measurePrettyLength());

// Terminate headers
client.println();

// Send body
root.prettyPrintTo(client);

3.1.3.11 prettyPrintTo —— 格式化輸出json數組字符串

函數說明

/**
 * 格式化輸出json數組字符串
 * @param buffer 內容輸出到內存區
 * @param size 內存區的大小
 * @param Print 打印流 比如 Serial
 * @param String 打印到字符串
 * @return 返回已寫大小
 */
size_t prettyPrintTo(char* buffer, size_t size) const;
size_t prettyPrintTo(char buffer[size]) const;
size_t prettyPrintTo(Print &) const;
size_t prettyPrintTo(String &) const;
size_t prettyPrintTo(std::string &) const;

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
array.add("hello");
array.add("world");
array.prettyPrintTo(Serial);

打印結果:

[
  "hello",
  "world"
]

3.1.3.12 printTo —— 壓縮式輸出json數組字符串

函數說明

/**
 * 壓縮式輸出json數組字符串
 * @param buffer 內容輸出到內存區
 * @param size 內存區的大小
 * @param Print 打印流 比如 Serial
 * @param String 打印到字符串
 * @return 返回已寫大小
 */
size_t printTo(char* buffer, size_t size) const;
size_t printTo(char buffer[size]) const;
size_t printTo(Print &) const;
size_t printTo(String &) const;
size_t printTo(std::string &) const;

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
array.add("hello");
array.add("world");
array.printTo(Serial);

打印結果:

["hello","world"]

注意

  • 該方法屬於壓縮式輸出,可以節省空間;

3.1.3.13 remove —— 移除某一個index位置的元素

函數說明

/**
 * 移除某一個index位置的元素
 * @param index 索引
 */
void remove(size_t index);

例子說明

JsonArray& array = jsonBuffer.createArray();
array.add("A");
array.add("B");
array.add("C");
array.remove(1);
array.printTo(Serial);

打印結果:

["A","C"]

注意

  • 該方法只會移除索引對應的value,但是並不會釋放對應的jsonbuffer空間,也不建議在循環中同時add和remove;

3.1.3.14 set —— 設置某一個index位置的值

函數說明

/**
 * 設置某一個index位置的值
 * @param index 索引位置
 * @param value 值
 * @return bool 是否設置成功
 */
bool set(size_t index, bool value);
bool set(size_t index, double value);
bool set(size_t index, float value);
bool set(size_t index, signed char value);
bool set(size_t index, signed int value);
bool set(size_t index, signed long value);
bool set(size_t index, signed short value);
bool set(size_t index, unsigned char value);
bool set(size_t index, unsigned int value);
bool set(size_t index, unsigned long value);
bool set(size_t index, unsigned short value);
bool set(size_t index, const char *value);
bool set(size_t index, char *value); // see Remarks
bool set(size_t index, const std::string &value); // see Remarks
bool set(size_t index, const String &value); // see Remarks
bool set(size_t index, const __FlashStringHelper *value); // see Remarks
bool set(size_t index, JsonArray &array);
bool set(size_t index, JsonObject &object);
bool set(size_t index, const JsonVariant &value);

例子說明

StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();

// increase the size of the array
array.add(666);
array.add(666);

// replace the values
array.set(0, "hello");
array.add(1, 3.14156);

// serialize
array.printTo(Serial);

打印結果:

["hello",3.14156]

3.1.3.15 size —— 返回json數組元素的個數

函數說明

/**
 * 返回json數組元素的個數
 * @return size_t 個數
 */
size_t size() const;

例子說明

JsonArray& array = jsonBuffer.createArray();
array.add("hello");
array.add("world");
Serial.println(array.size()); // 2

3.1.3.16 operator[] —— get/set 快捷快捷方式

函數說明

/**
 * get/set的快捷方式
 */
JsonVariant& operator[](size_t index);
const JsonVariant& operator[](size_t index) const;

例子說明

JsonArray& array = jsonBuffer.createArray();
array.add(42);
int value = array[0];
array[0] = 666;

3.1.3.17 success —— 判斷json數組是否成功分配內存或者解析

函數說明

/**
 * 判斷json數組是否成功分配內存或者解析
 * @return bool
 */
bool success() const;

例子說明

//Example 1: parsing success:
StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.parseArray("[1,2]");
Serial.println(array.success()); // true

//Example 2: parsing failure:
StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.parseArray("{1,2}");
Serial.println(array.success()); // false

//Example 3: allocation success:
StaticJsonBuffer<200> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
Serial.println(array.success()); // true

//Example 4: allocation failure:
StaticJsonBuffer<1> jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
Serial.println(array.success()); // false

3.1.4 編碼Json字符串

    講完理論知識,我們開始用具體例子來驗證。

實驗材料

  • Arduino板子,博主這裏用Mega2560

例子代碼

/**
 * 構造json例子
 * @author 單片機菜鳥
 * @date 2019/06/01
 */
#include <ArduinoJson.h>

void setup() {
  Serial.begin(115200);
  while (!Serial) continue;
  // Json對象對象樹的內存工具 靜態buffer 
  // 200 是大小 如果這個Json對象更加複雜,那麼就需要根據需要去增加這個值.
  StaticJsonBuffer<200> jsonBuffer;
  // StaticJsonBuffer 在棧區分配內存   它也可以被 DynamicJsonBuffer(內存在堆區分配) 代替
  // DynamicJsonBuffer  jsonBuffer;
  //創建最外層的json對象 —— root對象,頂節點
  JsonObject& root = jsonBuffer.createObject();
  //給最外層json對象添加屬性
  root["sensor"] = "gps";
  root["time"] = 1351824120;
  //在root對象中加入data數組
  JsonArray& data = root.createNestedArray("data");
  data.add(48.756080);
  data.add(2.302038);
  //在串口中打印,printTo方法不會格式化json
  root.printTo(Serial);
  // This prints:
  // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}

  Serial.println();
  //在串口中打印,prettyPrintTo方法會格式化json
  root.prettyPrintTo(Serial);
  // This prints:
  // {
  //   "sensor": "gps",
  //   "time": 1351824120,
  //   "data": [
  //     48.756080,
  //     2.302038
  //   ]
  // }
}

void loop() {
  // not used in this example
}

運行結果

image

3.1.5 解碼Json字符串

實驗材料

  • Arduino板子,博主這裏用Mega2560

例子代碼

/**
 * 解碼Json字符串
 * @author 單片機菜鳥
 * @date 2019/06/02
 */
#include <ArduinoJson.h>

void setup() {
  Serial.begin(115200);
  while (!Serial) continue;
  // Json對象對象樹的內存工具 靜態buffer 
  // 200 是大小 如果這個Json對象更加複雜,那麼就需要根據需要去增加這個值.
  StaticJsonBuffer<200> jsonBuffer;
  // StaticJsonBuffer 在棧區分配內存   它也可以被 DynamicJsonBuffer(內存在堆區分配) 代替
  // DynamicJsonBuffer  jsonBuffer;
  char json[] =
      "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
  JsonObject& root = jsonBuffer.parseObject(json);

  // Test if parsing succeeds.
  if (!root.success()) {
    Serial.println("parseObject() failed");
    return;
  }

  // Fetch values.
  //
  // Most of the time, you can rely on the implicit casts.
  // In other case, you can do root["time"].as<long>();
  const char* sensor = root["sensor"];
  long time = root["time"];
  double latitude = root["data"][0];
  double longitude = root["data"][1];

  // Print values.
  Serial.println(sensor);
  Serial.println(time);
  Serial.println(latitude, 6);
  Serial.println(longitude, 6);    
}

void loop() {
  // not used in this example
}

注意

  • 解析失敗通常有如下三個原因:
  1. Json字符串非法(格式不對)
  2. Json字符串產生對象
  3. JsonBuffer太小

3.2 ArduinoJson V5版本編譯配置

3.2.1 ARDUINOJSON_VERSION —— 當前ArduinoJson庫的具體版本

比如,如果開發者當前版本爲 5.13.2,那麼就會有:

#define ARDUINOJSON_VERSION "5.13.2" //完整版本號
#define ARDUINOJSON_VERSION_MAJOR 5 //大版本
#define ARDUINOJSON_VERSION_MINOR 13 //小版本
#define ARDUINOJSON_VERSION_REVISION 2 //小小版本

我們可以通過Serial打印版本號:

Serial.print("Using ArduinoJson version ");
Serial.println(ARDUINOJSON_VERSION);

通過它,我們可以做一些事情,比如:

  • 判斷是否安裝了ArduinoJson庫
#ifndef ARDUINOJSON_VERSION
#error ArduinoJson not found, please include ArduinoJson.h in your .ino file
#endif
  • 判斷某一個版本
#if ARDUINOJSON_VERSION_MAJOR!=5 || ARDUINOJSON_VERSION_MINOR<13
#error ArduinoJson 5.13+ is required
#endif

3.2.2 ARDUINOJSON_DEFAULT_NESTING_LIMIT —— Json數據的嵌套層數

  • 該選項用來配置 JsonBuffer::parseArray() 和 JsonBuffer::parseObject()的最大解析層數
  • ARDUINO 上默認是10層,一般來說不用改

3.2.3 ARDUINOJSON_ENABLE_ARDUINO_STRING —— 是否使能Arduino字符串功能

  • 默認true,建議不要動它

3.2.4 ARDUINOJSON_ENABLE_PROGMEM —— 是否使能Arduino F函數功能

  • 默認true,建議不要動它

3.2.5 ARDUINOJSON_ENABLE_PROGMEM —— 是否使能Arduino F函數功能

  • 默認true,建議不要動它

4.總結

    總體上,Json屬於一種數據交換格式,不會說太難。本篇屬於簡單介紹了ArduinoJson V5庫的使用,更加複雜的使用請直接去到ArduinoJson的主頁去查閱。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章