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的安裝 --建議使用靜態庫
- Linux下比較簡單。 jsoncpp項目里面有cmake配置文件。安裝比較簡單。
- Windows下比較麻煩點。推薦使用vcpkg安裝或者使用VS的nuget安裝。
Linux下編譯安裝
mkdir build && cd build && cmake ..
make
sudo make install
// 如果沒有生效
// sudo ldconfig
Windows下安裝和集成到vs
- 使用vcpkg
./vcpkg.exe install jsoncpp:x64-windows-static
我使用的靜態庫 - 在VS中配置 頭文件/庫文件路徑和鏈接器的輸入文件 jsoncpp.lib
- 我這里使用的是靜態庫,如果使用的是動態庫還需要在程序的運行目錄放入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"] = "zhangsan@qq.com";
mail["Google"] = "san.zhang@gmail.com";
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!!!
參考以及轉載處
- 參考以及轉載處 json簡介及JsonCpp用法
- 參考以及轉載處 新版jsoncpp的一些基本用法
- 參考以及轉載處 JsonCpp 的使用