1. 程式人生 > 其它 >jsoncpp的簡易教程

jsoncpp的簡易教程

目錄

Jsoncpp簡易教程

json簡介

json中僅支援兩種結構:

  • 物件
  • 陣列

“名稱/值”對的集合(A collection of name/value pairs)。一般稱為物件(object)。不同的語言中,它被理解為物件(object),紀錄(record),結構(struct),字典(dictionary),雜湊表(hash table),有鍵列表(keyed list),或者關聯陣列 (associative array)。

值的有序表,在大部分語言中,它被理解為陣列(array)。

你看到的json 要麼{}包圍,要麼[]包圍

{ 開頭說明是個json物件
[ 開頭說明 這是個json陣列

pair

一個pair(鍵值對)的通常結構是:
string : value

鍵值之間的對應關係使用 : 表示,左邊的為key,右邊的為value。
一般key使用字串,當然也可以使用數字,但是建議key只使用字串
value的取值就比較隨便,可以是任何任何json支援的型別(比如object,array,string,number,true/false,null等)這就為巢狀型別帶來了可能

object

object可以認為是多個pair的集合。
其語法是以{作為object開始,以}作為object結束,不同的pair之間使用,分割。
需要說明的是object中的資料儲存是無序的。(所有你生成的json,在解析打印出來不一定是你生成的json順序)

{
"name" : "tocy",
"age" : 1000
}

array

array是value的有序集合。
其語法是以[作為array起始,以]作為array結束,array元素之間使用,分割。
實際使用中建議在array中使用統一的型別,否則解析起來會麻煩點。

// 合法
[{"name":"tocy"}, {"age":1000}, {"domain":"cn"}]

// 也合法,但是不方便解析
[1, "ts", true, {"key":45}]

jsoncpp的安裝 --建議使用靜態庫

  1. Linux下比較簡單。 jsoncpp專案裡面有cmake配置檔案。安裝比較簡單。
  2. Windows下比較麻煩點。推薦使用vcpkg安裝或者使用VS的nuget安裝。

Linux下編譯安裝

mkdir build && cd build && cmake ..
make
sudo make install
// 如果沒有生效
// sudo ldconfig

Windows下安裝和整合到vs

  1. 使用vcpkg ./vcpkg.exe install jsoncpp:x64-windows-static 我使用的靜態庫
  2. 在VS中配置 標頭檔案/庫檔案路徑和連結器的輸入檔案 jsoncpp.lib
  3. 我這裡使用的是靜態庫,如果使用的是動態庫還需要在程式的執行目錄放入jsoncpp.dll

需要注意vcpkg的編譯方式(MT/MTd MD/MDd) 靜態庫是MT/MTd,動態庫是後者。

如果vs報錯4996,那是因為你使用了舊式的API,使用新式API或者關閉這個錯誤。

在VS專案配置中 C/C++高階。禁用特定警告輸入4996。 但是更加推薦使用新的API來解析和生成json。

專案使用

JsonCpp 中所有物件、類名都在 namespace Json 中, 只需要包含 json/json.h 即可。

注意需要連結哦,我是用的cmake target_link_libraries(UseJsoncpp -ljsoncpp)

jsoncpp核心介紹

JsonCpp 主要包含三種類型的 class: (老式API講解理論核心並不過時,這裡按照老式的來講,新式api只是為了安全而已)

  • Json::Value:可以表示所有支援的型別,如:int , double ,string , object, array等。其包含節點的型別判斷(isNull,isBool,isInt,isArray,isMember,isValidIndex等),型別獲取(type),型別轉換(asInt,asString等),節點獲取(get,[]),節點比較(過載<,<=,>,>=,==,!=),節點操作(compare,swap,removeMember,removeindex,append等)等函式。

  • Json::Reader:將檔案流或字串創解析到Json::Value中,主要使用parse函式。Json::Reader的建構函式還允許使用者使用特性Features來自定義Json的嚴格等級。

  • Json::Writer:與JsonReader相反,將Json::Value轉換成字串流等,Writer類是一個純虛類,並不能直接使用。在此我們使用 Json::Writer 的子類:Json::FastWriter(將資料寫入一行,沒有格式),Json::StyledWriter(按json格式化輸出,易於閱讀)

jsoncpp解析json

從檔案中解析json

真實場景就是當配置檔案是json時候

checkjson.json的內容為

{
"name" : "tocy",
"age" : 1000
}

這裡面儲存的是最簡單的object,我們可以使用下面程式碼將其讀入並解析:

void parse_from_file()
{
	std::ifstream ifs;
	ifs.open("/home/tudou/tmp/checkjson.json"); // Windows自己注意路徑吧
	assert(ifs.is_open());
	
	Json::Reader reader;
	Json::Value root;
    // 解析到root,root將包含Json裡所有子元素
	if (!reader.parse(ifs, root, false))
	{
		cerr << "parse failed \n";
		return;
	}
	string name = root["name"].asString(); // 實際欄位儲存在這裡, 因為知道是什麼型別,所以直接用asString(),沒有進行型別的判斷
	int age = root["age"].asInt(); // 這是整型,轉化是指定型別
	std::cout << name << ":" << age << "\n";
	ifs.close();
}

記憶體中解析json物件

void parse_mem_object() {

    const char json_data[] =
            "{\"name\" : \"Tocy\", \"salary\" : 100, \"msg\" : \"work hard\", \
        \"files\" : [\"1.ts\", \"2.txt\"]}";
#if 0
    // 推薦使用C++11的原始字串
    const std::string rawString = R"({"name" : "tony", "salary" : 100, "msg" : "work hard", "file" : [ "1.ts", "2.txt" ]})";
#endif
    Json::Reader reader;
    Json::Value root;
    // reader將Json字串解析到root,root將包含Json裡所有子元素,All!!!
    if (!reader.parse(json_data, json_data + sizeof(json_data), root)) {
        cerr << "json parse failed\n";
        return;
    }

    cout << "demo read from memory ---------\n";
    // 物件中key value
    string name = root["name"].asString();
    int salary = root["salary"].asInt();
    string msg = root["msg"].asString();
    cout << "name: " << name << " salary: " << salary;
    cout << " msg: " << msg << endl;
    cout << "enter files: \n";
    Json::Value files = root["files"]; // read array here
    // 注意這裡Json::Value files 我們新定義了一個Json::Value型別的變數
    // 陣列的解析寫法還有其他的方式:
    // root[arraykey][index][subkey] 可以這樣直接從根開始直接來操作
    for (unsigned int i = 0; i < files.size(); ++i) {
        // file[i] file[0] 陣列中的第一個元素
        cout << files[i].asString() << " ";
    }
    cout << endl << endl;
}
// 我們可以看到陣列的遍歷
// file[i] file[0] 陣列中的第一個元素
// 同理,如果json本身就是個陣列,那麼一開始就需要遍歷
// 看下面解析json陣列的操作吧

記憶體中解析json陣列


void parse_mem_array() {

#if 0
	const char json_data[] =
		"[{\"name\" : \"Tocy\", \"salary\" : 100}, {\"name\" : \"Kit\", \"salary\" : 89}, \
        \"a json note\"]";

#endif
	// 這裡我使用C++11帶來的原始字串
	const std::string json_date = R"([{"name":"jack","salary":100}, {"name":"kit","salary":200}, "a json note"])";

	Json::Reader reader;
	Json::Value root;
	// reader將Json字串解析到root,root將包含Json裡所有子元素
	if (!reader.parse(json_date, root)) {
		std::cerr << "parse fail! " << std::endl;
		return;
	}
	
	unsigned int count = root.size() - 1; // size() // Number of values in array or object
	
	// 最後一個比較特殊,所以i < count。 這也是為什麼建議陣列中的元素儘量是同一型別的原因
	for (unsigned int i = 0; i < count; i++) {
		// root[i]["name"] root[0]["name"] 第0個元素的name
		std::string name = root[i]["name"].asString();
		int salary = root[i]["salary"].asInt();
		cout << "name: " << name << " salary: " << salary << endl;
	}
	// 最後一個元素單獨處理
	cout << "last msg: " << root[count].asString() << endl;
}

jsoncpp封裝json

json封裝 簡易的演示

void demo_write_simple() {

	Json::Value root; // root
	Json::FastWriter writer;

	Json::Value Person; // 子Value
	Person["name"] = "tony";
	Person["age"] = 22;
	// 注意append方法的使用場景,只適用在新增陣列item
	root.append(Person);

	string jsonStr = writer.write(root); // json到字串

	cout << "demo_write_simple ==============\n";
	cout << jsonStr << endl; 
	// output: [{"age":22,"name":"tony"}]
}

json封裝 內嵌array的object

void demo_write_object() {
	Json::Value root;
	Json::FastWriter writer;

	root["name"] = "tocy";
	root["salary"] = 100;
	root["msg"] = "work hard";

	// 子value
	Json::Value files;
	files[0] = "1.ts";
	files[1] = "2.txt";

	root["files"] = files;
	std::string json_data = writer.write(root);
	cout << "demo write json object ==============\n";
	cout << json_data << endl;
}
// {"files":["1.ts","2.txt"],"msg":"work hard","name":"tocy","salary":100}

json封裝 內嵌object的array

// tips: 構建一個json檔案是json物件還是json陣列。
// 就看開始的根root,如果開始root[0] 明顯是陣列了
void demo_write_array() {

	Json::Value root;
	Json::FastWriter writer;
	// 物件1
	// 使用{}只是為了隔離作用域
	{
		Json::Value person;
		person["name"] = "jack";
		person["salary"] = 200;
		// index start 0
		root[0] = person;
	}

	// 物件2
	{
		Json::Value person;
		person["name"] = "miss";
		person["salary"] = 1000;
		//
		root[1] = person;
	}

	root[2] = "a json note";

	// to String Json::String等價於std::string
	string json_data = writer.write(root);
	cout << "demo write json ==============\n";
	cout << json_data << endl;
}

jsoncpp的新式API的使用

封裝json

// 建立json字串 新式API的使用
std::string createJson()
{
    std::string jsonStr;
    Json::Value root, language, mail;
    Json::StreamWriterBuilder writerBuilder; // 新式API
    std::ostringstream os;

    // 設定預設無格式化的輸出
    writerBuilder.settings_["indentation"] = "";

    root["Name"] = "Zhangsan";
    root["Age"] = 25;

    language[0] = "C";
    language[1] = "C++";
    root["Language"] = language;

    mail["QQ"] = "[email protected]";
    mail["Google"] = "[email protected]";
    root["E-mail"] = mail;

    root["Industry"] = "IT";

    // 這裡使用智慧指標
    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    jsonWriter->write(root, &os); // json-->stringstream
    jsonStr = os.str(); // 轉為string

    // 無格式化的輸出
    std::cout << "Json-none:\n" << jsonStr << std::endl;
    // 格式化的輸出
    std::cout << "Json-formatted:\n" << root.toStyledString() << std::endl;

    return jsonStr;
}

解析json

bool parsrJSON()
{
    const std::string rawString = R"({"name" : "tony", "salary" : 100, "msg" : "work hard"})";

    Json::Value root;
    Json::String errs;
    Json::CharReaderBuilder readBuilder; //
    std::unique_ptr<Json::CharReader> jsonRead(readBuilder.newCharReader());
    if (!jsonRead) {
        std::cerr << "jsonRead is null" << std::endl;
        return false;
    }
    // reader將Json字串解析到root,root將包含Json裡所有子元素
    bool ret = jsonRead->parse(rawString.c_str(), rawString.c_str() + rawString.length(), &root, &errs);
    if (!ret || !errs.empty()) {
        std::cout << "parseJsonFromString error!" << errs << std::endl;
        return false;
    }
    
    cout << "parsrJSON() read from memory using object start ! ---------\n";
    // 看一下物件中key value
    string name = root["name"].asString();
    int salary = root["salary"].asInt();
    string msg = root["msg"].asString();
    cout << "name: " << name << " salary: " << salary;
    cout << " msg: " << msg << endl;
    cout << "parsrJSON() read from memory using object  end !---------\n";
    return true;
}

給json新增欄位(其實就是解析再封裝)

std::string parsrJSONAddItem(const std::string& rawString)
{
#if 0
    // 傳遞的rawString內容如下:
    const std::string rawString = R"({"name" : "tony", "salary" : 100, "msg" : "work hard"})";
#endif
    // 先解析
    Json::Value root;
    Json::String errs;
    Json::CharReaderBuilder readBuilder;
    std::unique_ptr<Json::CharReader> jsonRead(readBuilder.newCharReader());
    if (!jsonRead) {
        std::cerr << "jsonRead is null" << std::endl;
        return std::string("");
    }

    // reader將Json字串解析到root,root將包含Json裡所有子元素
    bool ret = jsonRead->parse(rawString.c_str(), rawString.c_str() + rawString.length(), &root, &errs);
    if (!ret || !errs.empty()) {
        std::cout << "parseJsonFromString error!" << errs << std::endl;
        return std::string("");
    }
    cout << "demo read from memory using object ---------\n";

    // 看一下物件中key value
    string name = root["name"].asString();
    int salary = root["salary"].asInt();
    string msg = root["msg"].asString();
    cout << "name: " << name << " salary: " << salary << " msg: " << msg << endl;

    // 新增兩個欄位
    root["from"] = "sdasdasd";
    root["to"] = "sadd22eewewe3";

    // 重新儲存到字串
    std::string jsonStr;
    Json::StreamWriterBuilder writerBuilder;
    std::ostringstream os;
 
    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    jsonWriter->write(root, &os);
    jsonStr = os.str();

    std::cout << jsonStr << std::endl;

    return jsonStr;
}

jsoncpp其他常用的api

  • 判斷json字串中是否存在某鍵值的幾種方法
//  use isNull
if (!root["key"].isNull()) //
{
    std::string strValue= root["key"].asString(); // 已知key的value是string型別
    std::cout << strValue<< std::endl; 
}
// use isMember
// jsoncpp判斷Value中是否含有指定的key
bool HasMember1(Json::Value& value, string key)
{
    return value.isMember(key);
}
  • 刪除json中的物件
root.removeMember("key"); // 可以直接使用value的removeMember方法刪除指定的key
  • 型別判斷 型別轉換各種常用的api
// 注:程式碼來自這位博主:https://www.cnblogs.com/dswcnblog/p/6678708.html
// 寫的非常的好
    if (reader.parse(str, value)) {
        //節點判斷
        std::cout << "value's empty:" << value.empty() << std::endl;
        std::cout << "name is string:" << value["name"].isString() << std::endl;
        std::cout << "age is string:" << value["age"].isString() << std::endl;

        //型別獲取
        std::cout << "name's type:" << value["name"].type() << std::endl;
        std::cout << "like's type:" << value["like"].type() << std::endl;

        //型別轉換
        //根據Key獲取值時最好判斷型別,否則解析會中斷
        std::cout << "name:" << value["name"].asString() << std::endl;
        std::cout << "age:" << value["age"].asInt() << std::endl;

        //節點獲取
        std::cout << value["job"] << std::endl;                        //[]方式獲取
        std::cout << value.get("name", "dxx") << std::endl;            //get方式獲取
        std::cout << value.isMember("job") << std::endl;
        std::cout << "value's obj:" << value.isObject() << std::endl;
        std::cout << "like's obj:" << value["like"].isObject() << std::endl;

        std::cout << "like.size:" << value["like"].size() << std::endl;
        std::cout << "like[0][food]:" << value["like"][0]["food"].asString() << std::endl;

        //節點操作
        std::cout << "name compare age:" << value["name"].compare("age") << std::endl;
        value["name"] = "swduan";            //修改
        value["address"] = "hz";             //增加
        value["phone"] = "10086";        
        value.removeMember("age");           //刪除
        value["like"][0]["sport"] = "game";  //往value["like"]中新增一項元素

        Json::Value item;
        item["hate"] = "game";
        value["like"].append(item);            //value["like"]中再新增一維陣列
        std::cout << "value[\"like\"]'s size:" << value["like"].size() << std::endl;
        
        std::cout << "--------------------" << std::endl;
        std::cout << value.toStyledString() << std::endl;

        std::cout << "--------------------" << std::endl;
        auto all_member = value.getMemberNames();
        for (auto member : all_member) {
            std::cout << member << std::endl;
        }

        std::cout << "--------------------" << std::endl;
        value.clear();        //清空元素
        std::cout << value.toStyledString() << std::endl;
    }

注意事項

  • Value的size() 函式的返回值是返回值是 unsigned int; 可以點進去看下定義
    那麼我們在for迴圈的時候就需要注意了!
    Json::Value files = root["files"]; // read array here
    // 需要注意的files.size() 的返回值是 unsigned int; 所以我們i 也需要定義為unsigned int 型別
    for (unsigned int i = 0; i < files.size(); ++i) {
        // file[i] file[0] 陣列中的第一個元素
        cout << files[i].asString() << " ";
    }
  • 對不存在的鍵獲取值會返回此型別的預設值
  • 通過key獲取value時,要先判斷value的型別,使用錯誤的型別獲取value會導致程式中斷。
  • 獲取json陣列中某一項key的value應該使用value[arraykey][index][subkey]獲取或迴圈遍歷陣列獲取。(所以我們也可以直接從root直接拿陣列的資訊)
  • append函式功能是將Json::Value新增到陣列末尾。just for array!!!

參考以及轉載處