1. 程式人生 > >【C++】解析Json

【C++】解析Json

本文將介紹
① JSON簡介
② Jsoncpp讀寫操作
③ Jsoncpp函式介紹
④ Jsoncpp示例程式設計(VS2015)   下載示例

1. JSON簡介

JSON(JavaScript Object Notation) 是一種輕量級的資料交換格式,易於人閱讀和編寫。JSON語法是 JavaScript 物件表示語法的子集,所以在Java,JavaScript等語言中使用起來是十分方便的,它們底層支援JSON讀寫。

JSON的官方網址 http://www.json.org/

JSON語法格式

JSON是鍵值對,資料書寫格式是:名稱/值對,名稱/值對包括欄位名稱(在雙引號中),後面寫一個冒號,然後是值

"firstName": "Tommy"

JSON的值可以是數字、字串、布林邏輯值、陣列、物件或null,物件在花括號中書寫:

{"firstName":"Tommy" , "lastName":"Arm"}

陣列在方括號中書寫,陣列可包含多個物件:

{
    "employees":[
        { "name":"John" , "Age":26},
        { "name":"Anna" , "Age ":28 },
        { "name":"Peter" , "Age ":28 }
        ]
}

注意每個物件(花括號)中,鍵值對的鍵名是唯一的,下面寫法會報錯:

{ "firstName":"Tommy", " firstName ":"Arm" }

2. Jsoncpp讀寫操作

C++需要讀寫JSON檔案,可使用Jsoncpp開源庫,這個開源庫是跨平臺的。JSON的開源庫很多,在官方網站底下有各種語言的Json開源庫連結,推薦使用Jsoncpp。
Jsoncpp下載地址:https://github.com/open-source-parsers/jsoncpp

Jsoncpp有三個主要類:Value,Reader,Writer。注意Json::Value只能處理ANSI型別的字串,如果程式使用Unicode編碼,需要加一個轉碼函式。

工程使用Jsoncpp方法有倆

使用方法一:使用Jsoncpp生成的lib檔案

解壓下載的Jsoncpp檔案,在jsoncpp-master /makefiles/msvc2010目錄裡找到jsoncpp.sln,用VS2010版本編譯,預設生成靜態連結庫。在工程中引用,只需要包含include/json下的標頭檔案及生成的.lib檔案即可。
在需要使用的cpp

包含lib檔案:#pragmacomment(lib."json_msvc2010_libmt.lib")

缺點:Debug模式和Release模式的Lib檔案不一樣,每次都要生成Lib

使用方法二(推薦):使用Jsoncpp的h檔案和cpp檔案

解壓下載的Jsoncpp檔案,拷貝jsoncpp-master\include\json中的檔案和jsoncpp-master\src\lib_json中的檔案,將所有檔案匯入需要用的工程中。

直接使用h/cpp檔案需要一些改動:

① 將json_reader.cpp、json_value.cpp、json_writer.cpp三個檔案的PrecompiledHeader屬性設定為不包含標頭檔案(Not Using Precompiled Headers),否則可能出現編譯錯誤。

② 工程中的檔案包含路徑更改去掉 json/ 字首,尖括號改成雙引號。

例如json_tool.h中的#include<json/config.h>  更改為#include"config.h"
json_reader.h  json_writer.h中的標頭檔案也做相應修改

示例工程已整理庫可直接使用,匯入json資料夾,然後在需要使用的地方包含標頭檔案#include "json\json.h"

Jsoncpp寫入資料

宣告一個根變數JsonRoot,這個根是操作的控制代碼

// 宣告Json變數,這個作為根
Json::Value JsonRoot;

寫入簡單的數字型和字元型變數

// 寫入字串
JsonRoot["name"] = Json::Value("Denny");
// 寫入整型
JsonRoot["age"] = Json::Value(22);
// 寫入浮點型數字
JsonRoot["height"] = Json::Value(1.78);
// 寫入布林型
JsonRoot["play_football"] = Json::Value(true);
// 寫入Json物件
Json::Value JsonObj;
JsonObj["sometime"] = Json::Value(2018);
JsonObj["someone"] = Json::Value("Kelly");
JsonObj["somewhere"] = Json::Value("city");
JsonRoot["object"] = JsonObj;

寫入複雜的陣列型別

// 單個鍵寫入數字陣列
JsonRoot["number_array"].append(1);
JsonRoot["number_array"].append(2);
JsonRoot["number_array"].append(3);
JsonRoot["number_array"].append(4);
// 單個鍵寫入字串陣列
JsonRoot["string_array"].append("string01");
JsonRoot["string_array"].append("string02");
JsonRoot["string_array"].append("string03");
// 寫入Json物件陣列,即陣列由物件構成
Json::Value JsonArr1, JsonArr2, JsonArr3;
JsonArr1["string1"] = Json::Value("1-1");
JsonArr1["string2"] = Json::Value("1-2");
JsonArr2["string1"] = Json::Value("2-1");
JsonArr2["string2"] = Json::Value("2-2");
JsonArr3["string1"] = Json::Value("3-1");
JsonArr3["string2"] = Json::Value("3-2");
JsonRoot["object_array"].append(JsonArr1);
JsonRoot["object_array"].append(JsonArr2);
JsonRoot["object_array"].append(JsonArr3);
簡單型別和陣列型別基本滿足使用,對更復雜的型別讀寫本文不作介紹。
Jsoncpp讀取資料

Jsoncpp新版有一個大改動,Json::Reader不能直接用了,會報error C4996,改為使用Json::CharReader.

舊版本用法

// stringJson是Json字串
string stringJson;
Json::Reader reader;
Json::Value root;
JsonReader.parse(stringJson, root);

新版本用法

// stringJson是Json字串
Json::CharReaderBuilder b;
Json::CharReader* JsonReader(b.newCharReader());
Json::Value JsonRoot, ObjectTmp;
JSONCPP_STRING errs;
const char* pstr = stringJson.c_str();
bool ok = JsonReader->parse(pstr, pstr +strlen(pstr), &JsonRoot, &errs);

讀操作前先定義幾個讀取函式,防止讀取型別與寫入型別不匹配而崩潰。

int Json_ReadInt(Json::Value JV, int ori_value = 0)
{
	int result = ori_value;
	Json::ValueType VT = JV.type();
	if (VT == Json::ValueType::intValue)
		result = JV.asInt();
	return result;
}
double Json_ReadDouble(Json::Value JV, double ori_value = 0.0)
{
	double result = ori_value;
	Json::ValueType VT = JV.type();
	if (VT == Json::ValueType::realValue)
		result = JV.asDouble();
	return result;
}
string Json_ReadString(Json::Value JV, string ori_value = "")
{
	string result = ori_value;
	Json::ValueType VT = JV.type();
	if (VT == Json::ValueType::stringValue)
		result = JV.asCString();
	return result;
}
bool Json_ReadBool(Json::Value JV, bool ori_value = true)
{
	bool result = ori_value;
	Json::ValueType VT = JV.type();
	if (VT == Json::ValueType::booleanValue)
		result = JV.asBool();
	return result;
}

讀取簡單的數字型和字元型變數

string stringTmp;
int intTmp;
double doubleTmp;
bool boolTmp;
// 讀取字串
stringTmp = Json_ReadString(JsonRoot["name"]);
// 讀取整型
intTmp = Json_ReadInt(JsonRoot["age"]);
// 讀取浮點型數字
doubleTmp = Json_ReadDouble(JsonRoot["height"]);
// 讀取布林型
boolTmp = Json_ReadBool(JsonRoot["play_football"]);
// 讀取Json物件
Json::Value JsonObj = JsonRoot["object"];
intTmp = Json_ReadInt(JsonObj["sonetime"]);
stringTmp = Json_ReadString(JsonObj["someone"]);
stringTmp = Json_ReadString(JsonObj["somewhere"]);

讀取複雜的陣列型別

// 讀取數字陣列,先讀取陣列物件,然後在物件內遍歷
Json::Value ArrInt = JsonRoot["number_array"];
for (size_t i = 0; i < ArrInt.size(); i++)
{
    intTmp = Json_ReadInt(ArrInt[i]);
}
// 讀取字串陣列
Json::Value ArrString = JsonRoot["string_array"];
for (size_t j = 0; j < ArrString.size(); j++)
{
    stringTmp = Json_ReadString(ArrString[j]);
}
// 讀取Json物件陣列
Json::Value ObjectArray;
ObjectArray = JsonRoot["object_array"];
for (size_t k = 0; k < ObjectArray.size(); k++)
{
    stringTmp = Json_ReadString(ObjectArray[k]["string1"]);
    stringTmp = Json_ReadString(ObjectArray[k]["string2"]);
}

3.Jsoncpp函式介紹

下面介紹一些常用的函式

Value::getMemberNames()
獲取物件一級子層的名稱,如果根物件呼叫,則返回首層名稱列表,如果是子層呼叫,則返回子層下一級列表。

// 例子:判斷子節點是不是浮點型
std::vector<std::string> strNamelist = JsonRoot.getMemberNames();
int iSize = strNamelist.size();
for (int i = 0; i < iSize; ++i)
{
	if (JsonRoot[strNamelist[i]].isDouble())
	    //... do something
        else
            //... do something
 }

Value:: isNull() / isBool() / isInt() / isDouble() / isString() / isArray() / isObject() etc..
判斷Json節點物件型別:JsonRoot[node].isInt()

Value:: asString() / asInt() / asDouble / asBool()
將Json資料轉為對應型別

int i = JsonRoot["int"].asInt();
double db = JsonRoot["double"].asDouble();
string str = JsonRoot["string"].asString();
bool b = JsonRoot["bool"].asBool();

Value::toStyledString()
將Json物件的資料格式化為字串,用於寫入檔案。

Value::removeMember( const std::string &key )
移除指定節點

Value::isMember( const char *key )
判斷指定名稱是不是Json內的節點鍵名

Value::setComment( const std::string &comment, CommentPlacement placement )
Value::hasComment( CommentPlacement placement )
Value::getComment( CommentPlacement placement )
關於註釋的一些函式,placement是位置列舉型別。

CharReader::parse(char const* beginDoc, char const* endDoc, Value* root, JSONCPP_STRING* errs)
將Json字串匯入到Json物件,用法見上文。

4.Jsoncpp示例程式設計

工程實現平臺VS2015,Jsoncpp版本1.8.4,下載連結在頂部