使用C++處理JSON數據交換格式
一、摘要
JSON的全稱爲:JavaScript Object Notation,顧名思義,JSON是用於標記Javascript對象的,JSON官方的解釋爲:JSON是一種輕量級的數據傳輸格式。本文並不詳細介紹JSON本身的細節,旨在討論如何使用C++語言來處理JSON。關於JSON更具體的信息,可參見JSON官網:http://www.json.org。http://json.org/json-zh.html
二、本文選擇處理JSON的C++庫
本文選擇一個第三方庫jsoncpp來解析JSON。jsoncpp是比較出名的C++ JSON解析庫。在JSON官網也是首推的。下載地址爲:http://sourceforge.net/projects/jsoncpp。本文使用的jsoncpp版本爲:0.5.0。
三、jsoncpp在Windows下的編譯
方法一:使用Jsoncpp生成的lib文件
解壓上面下載的Jsoncpp文件,在jsoncpp-src-0.5.0/makefiles/vs71目錄裏找到jsoncpp.sln,用VS2008版本編譯,默認生成靜態鏈接庫。 在工程中引用,只需要包含include/json下的頭文件及生成的.lib文件即可。 如何包含lib文件:在.cpp文件中#pragma comment(lib."json_vc71_libmt.lib"),
在工程屬性中Linker下Input中Additional Dependencies寫入lib文件名字(Release下爲json_vc71_libmt.lib,Debug爲json_vc71_libmtd.lib)
注意:Jsoncpp的lib工程編譯選項要和VS工程中的編譯選項保持一致。如lib文件工程編譯選項爲MT(或MTd),VS工程中也要選擇MT(或MTd),否則會出現編譯錯誤問題,debug和release下生成的lib文件名字不同,注意不要看錯了,當成一個文件來使用(我就犯了這個錯誤)。
方法二:使用Jsoncpp包中的.cpp和.h文件
解壓上面下載的Jsoncpp文件,把jsoncpp-src-0.5.0文件拷貝到工程目錄下,將jsoncpp-src-0.5.0\jsoncpp-src-0.5.0\include\json和jsoncpp-src-0.5.0\jsoncpp-src-0.5.0\src\lib_json目錄裏的文件包含到VS工程中,在VS工程的屬性C/C++下General中Additional Include Directories包含頭文件目錄.\jsoncpp-src-0.5.0\include。在使用的cpp文件中包含json頭文件即可,如:#include "json/json.h"。將json_reader.cpp、json_value.cpp和json_writer.cpp三個文件的Precompiled Header屬性設置爲Not Using Precompiled Headers,否則編譯會出現錯誤。
jsoncpp 使用詳解
jsoncpp 主要包含三種類型的 class:Value、Reader、Writer。jsoncpp 中所有對象、類名都在 namespace Json 中,包含 json.h 即可。
Json::Value 只能處理 ANSI 類型的字符串,如果 C++ 程序是用 Unicode 編碼的,最好加一個 Adapt 類來適配。
本實驗採用方法二, 這樣方便調試,同時也方便學習該開源庫的代碼。 本代碼不爲商用, 所以以學習爲主。
四\ 需要解析的數據,face++ 調動返回json數據,數據個數如下:
{
"face": [
{
"attribute": {
"age": {
"range": 5,
"value": 23
},
"gender": {
"confidence": 99.9999,
"value": "Female"
},
"glass": {
"confidence": 99.945,
"value": "None"
},
"pose": {
"pitch_angle": {
"value": 17
},
"roll_angle": {
"value": 0.735735
},
"yaw_angle": {
"value": -2
}
},
"race": {
"confidence": 99.6121,
"value": "Asian"
},
"smiling": {
"value": 4.86501
}
},
"face_id": "17233b4b1b51ac91e391e5afe130eb78",
"position": {
"center": {
"x": 49.4,
"y": 37.6
},
"eye_left": {
"x": 43.3692,
"y": 30.8192
},
"eye_right": {
"x": 56.5606,
"y": 30.9886
},
"height": 26.8,
"mouth_left": {
"x": 46.1326,
"y": 44.9468
},
"mouth_right": {
"x": 54.2592,
"y": 44.6282
},
"nose": {
"x": 49.9404,
"y": 38.8484
},
"width": 26.8
},
"tag": ""
}
],
"img_height": 500,
"img_id": "22fd9efc64c87e00224c33dd8718eec7",
"img_width": 500,
"session_id": "38047ad0f0b34c7e8c6efb6ba39ed355",
"url": "http://www.faceplusplus.com.cn/wp-content/themes/faceplusplus/assets/img/demo/1.jpg?v=4"
}
五、遇到問題,曾經嘗試多次,找了很多博客都是不能解決問題,
仔細觀察後,發現有[ ], 這個不能忽視。http://blog.163.com/pei_hua100/blog/static/80569759201333114010800/ 通過這個博客啓發。發現[] 爲裏面的數據。所以更改代碼如下。
<span style="color:#333333;">Json::Value detect;
Json::Value face;
if (!DetectResult1.parse(DetectResult, detect))
{
return -1;
}
int face_size = detect["face"].size();
// 遍歷face 個數
for (int i = 0; i < face_size; i++) </span><span style="color:#ff0000;"> 這裏很關鍵,學會使用該方法。</span><span style="color:#333333;">
{
Json::Value attribute;
attribute = detect["face"][i]["attribute"];
int attribute_size = attribute.size();
int age = attribute["age"]["value"].asInt();
string gender = attribute["gender"]["value"].asString();
int smiling = attribute["smiling"]["value"].asInt();
string race = attribute["race"]["value"].asString();
}
for (int i = 0; i < face_size; i++)
{
Json::Value position;
position = detect["face"][i]["position"];
int centerX = position["center"]["x"].asInt();
int centerY = position["center"]["y"].asInt();
int eye_leftx = position["eye_left"]["x"].asInt();
int eye_lefty = position["eye_left"]["y"].asInt();
int eye_rightx = position["eye_right"]["x"].asInt();
int eye_righty = position["eye_right"]["y"].asInt();
int height = position["height"].asInt();
cout << "centerX" << centerX << "centerY" << centerY << endl;
int mouth_leftx = position["mouth_left"]["x"].asInt();
int mouth_lefty = position["mouth_left"]["y"].asInt();
int mouth_rightx = position["mouth_right"]["x"].asInt();
int mouth_righty = position["mouth_right"]["y"].asInt();
int nosex = position["nose"]["x"].asInt();
int nosey = position["nose"]["y"].asInt();
int width = position["width"].asInt();
}
int img_height = detect["img_height"].asInt();
cout << "img_height" << img_height << endl;
int img_width = face["img_width"].asInt();
</span>
通過這個方式,發現curl 返回的代碼 裏面有\n 換行符, 想辦法去掉他。
本實驗的實現方式如下:
// 去掉返回值中的 \n 換行符
string::iterator it;
for (it = HTTPRESULT.begin(); it != HTTPRESULT.end(); ++it)
{
if (*it == '\n')
{
// *it = '\\r\\n';
HTTPRESULT.erase(it);
}
}
本次實驗所有代碼如下:
// face++.cpp : 定義控制檯應用程序的入口點。
//
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
#include "HttpClient.h"
#include "curl/curl.h"
#include "curl/easy.h"
std::string strResult;
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include "json/json.h"
using namespace cv;
// #pragma comment(lib, "libcurl.lib")
std::string HTTPRESULT;
const char* detectResult;
void Write_data(void* buffer, size_t size, size_t nmemb, void* user_p){
cout << "(const char*)buffer" << (const char*)buffer << endl;
HTTPRESULT += (const char*)buffer;
detectResult = (const char*)buffer;
}
int _tmain(int argc, _TCHAR* argv[])
{
CURL *curl = curl_easy_init();
CURLcode res = curl_global_init(CURL_GLOBAL_WIN32);
struct curl_httppost *formpost = NULL;
struct curl_httppost *lastptr = NULL;
// struct curl_slist *headerlist=NULL;
// static const char buf[] = "Expect:";
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "api_key",
CURLFORM_COPYCONTENTS, "d45344602f6ffd77baeab05b99fb7730",
CURLFORM_END);
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "api_secret",
CURLFORM_COPYCONTENTS, "jKb9XJ_GQ5cKs0QOk6Cj1HordHFBWrgL",
CURLFORM_END);
char* file_data = NULL;
long file_size = 0;
string imageName = "d:\\mqx.jpg";
FILE* fp = fopen("d:\\mqx.jpg", "rb");
if (fp)
{
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
file_data = new char[file_size + 1];
fread(file_data, 1, file_size, fp);
cout << file_data << endl;
fclose(fp);
}
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "img",
CURLFORM_BUFFER, "test.jpg",
CURLFORM_BUFFERPTR, file_data,
CURLFORM_BUFFERLENGTH, file_size,
CURLFORM_CONTENTTYPE, "image/jpeg",
CURLFORM_END);
if (curl) {
// what URL that receives this POST
curl_easy_setopt(curl, CURLOPT_URL, "http://apicn.faceplusplus.com/v2/detection/detect");
curl_easy_setopt(curl, CURLOPT_VERBOSE, 0);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &HTTPRESULT);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Write_data);
cout << "CURLOPT_WRITEFUNCTION" << CURLOPT_WRITEFUNCTION << endl;
char error[1024];
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
res = curl_easy_perform(curl);
if (res != CURLE_OK) cout << endl << error << endl;
}
curl_easy_cleanup(curl);
curl_formfree(formpost);
cout << endl << HTTPRESULT<< endl;
// cout << endl << "測試結果如下" << endl;
// cout << endl << "年齡:" << HTTPRESULT.find("value") << endl;
// cout << endl << "年齡:" << HTTPRESULT.substr(HTTPRESULT.find("age", 0)) << endl;
// cout << endl << "年齡:" << HTTPRESULT.substr(HTTPRESULT.find("gender",0)) << endl;
// cout << endl << "年齡:" << HTTPRESULT.substr(HTTPRESULT.find("race", 0)) << endl;
// cout << endl << "年齡:" << HTTPRESULT.substr(HTTPRESULT.find("smiling", 0)) << endl;
// 得到年齡性別等。
int agePos = HTTPRESULT.find("age", 0);
string age = HTTPRESULT.substr(agePos + 70, 2);
int genderPos = HTTPRESULT.find("gender", 0);
string gender = HTTPRESULT.substr(genderPos + 84, 6);
int racePos = HTTPRESULT.find("race", 0);
string race = HTTPRESULT.substr(racePos + 83, 5);
int smilingPos = HTTPRESULT.find("smiling", 0);
string smiling = HTTPRESULT.substr(smilingPos + 41, 5);
// end 結束
/*
//讀入圖像
Mat img = imread(imageName, CV_LOAD_IMAGE_COLOR);
//如果讀入圖像失敗
if (img.empty())
{
cout << "Could not open or find the image!" << endl;
return -1;
}
//創建窗口
namedWindow("face", CV_WINDOW_AUTOSIZE);
//顯示圖像
imshow("face", img);
//等待按鍵,按鍵盤任意鍵返回
waitKey(-1);
// while (HTTPRESULT != " ")
// {
// agePos[numage] = HTTPRESULT.find("value");
// HTTPRESULT
// }
// int agePos = HTTPRESULT.find("value");
if (file_data != NULL)
delete[] file_data;
*/
//HTTPRESULT = HTTPRESULT.replace(/ \\n / g, "\\n");
// 去掉返回值中的 \n 換行符
string::iterator it;
for (it = HTTPRESULT.begin(); it != HTTPRESULT.end(); ++it)
{
if (*it == '\n')
{
// *it = '\\r\\n';
HTTPRESULT.erase(it);
}
}
const char * DetectResult = HTTPRESULT.c_str();
Json::Reader DetectResult1;
Json::Value detect;
Json::Value face;
if (!DetectResult1.parse(DetectResult, detect))
{
return -1;
}
int face_size = detect["face"].size();
// 遍歷face 個數
for (int i = 0; i < face_size; i++)
{
Json::Value attribute;
attribute = detect["face"][i]["attribute"];
int attribute_size = attribute.size();
int age = attribute["age"]["value"].asInt();
string gender = attribute["gender"]["value"].asString();
int smiling = attribute["smiling"]["value"].asInt();
string race = attribute["race"]["value"].asString();
}
for (int i = 0; i < face_size; i++)
{
Json::Value position;
position = detect["face"][i]["position"];
int centerX = position["center"]["x"].asInt();
int centerY = position["center"]["y"].asInt();
int eye_leftx = position["eye_left"]["x"].asInt();
int eye_lefty = position["eye_left"]["y"].asInt();
int eye_rightx = position["eye_right"]["x"].asInt();
int eye_righty = position["eye_right"]["y"].asInt();
int height = position["height"].asInt();
cout << "centerX" << centerX << "centerY" << centerY << endl;
int mouth_leftx = position["mouth_left"]["x"].asInt();
int mouth_lefty = position["mouth_left"]["y"].asInt();
int mouth_rightx = position["mouth_right"]["x"].asInt();
int mouth_righty = position["mouth_right"]["y"].asInt();
int nosex = position["nose"]["x"].asInt();
int nosey = position["nose"]["y"].asInt();
int width = position["width"].asInt();
}
int img_height = detect["img_height"].asInt();
cout << "img_height" << img_height << endl;
int img_width = face["img_width"].asInt();
system("pause");
return 0;
}
下面是從網上找的代碼示例:
1. 從字符串解析json
要使用第三方源碼庫,第一步少不了的就是編譯,將源碼文件編譯成我們方便使用的動態鏈接庫、靜態鏈接庫或者靜態導入庫[1]。
jsconcpp進行JSON解析的源碼文件分佈在include/json、src/lib_json下。其實jsoncpp源碼並不多,爲了方便產品管理,此處沒必要將其編譯爲動態鏈接庫或者靜態導入庫,所以我們選擇使用靜態鏈接庫[2]。
jsoncpp已經處理的很完善了,所有編譯選項都已經配置好,打開makefiles/vs71/jsoncpp.sln便可以開始編譯(默認是使用VS2003編譯器的,打開時直接按照VS2005提示轉換即可)。
四、jsoncpp使用詳解
jsoncpp主要包含三種類型的class:Value、Reader、Writer。jsoncpp中所有對象、類名都在namespace Json中,包含json.h即可。
Json::Value只能處理ANSI類型的字符串,如果C++程序是用Unicode編碼的,最好加一個Adapt類來適配。
1、Value
Json::Value是jsoncpp中最基本、最重要的類,用於表示各種類型的對象,jsoncpp支持的對象類型可見Json::ValueType枚舉值。
可如下是用Json::Value類:
Json::Value json_temp; //臨時對象,供如下代碼使用
json_temp["name"] = Json::Value("huchao");
json_temp["age"] = Json::Value(26);
Json::Value root; //表示整個json對象
root["key_string"] = Json::Value("value_string"); //新建一個Key(名爲:key_string),賦予字符串值:"value_string"。
root["key_number"] = Json::Value(12345); //新建一個Key(名爲:key_number),賦予數值:12345。
root["key_boolean"] = Json::Value(false); //新建一個Key(名爲:key_boolean),賦予bool值:false。
root["key_double"] = Json::Value(12.345); //新建一個Key(名爲:key_double),賦予double值:12.345。
root["key_object"] = Json_temp; //新建一個Key(名爲:key_object),賦予json::Value對象值。
root["key_array"].append("array_string"); //新建一個Key(名爲:key_array),類型爲數組,對第一個元素賦值爲字符串:"array_string"。
root["key_array"].append(1234); //爲數組key_array賦值,對第二個元素賦值爲:1234。
Json::ValueType type = root.type(); //獲得root的類型,此處爲objectValue類型。
注:跟C++不同,JavaScript數組可以爲任意類型的值,所以jsoncpp也可以。
如上幾個用法已經可以滿足絕大部分json應用了,當然jsoncpp還有一些其他同能,比如說設置註釋、比較json大小、交換json對象等,都很容易使用,大家自己嘗試吧。
2、Writer
如上說了Json::Value的使用方式,現在到了該查看剛纔賦值內容的時候了,查看json內容,使用Writer類即可。
Jsoncpp的Json::Writer類是一個純虛類,並不能直接使用。在此我們使用Json::Writer的子類:Json::FastWriter、Json::StyledWriter、Json::StyledStreamWriter。
顧名思義,用Json::FastWriter來處理json應該是最快的,下面我們來試試。
Json::FastWriter fast_writer;
std::cout << fast_writer.write(root) << std::endl;
輸出結果爲:
{"key_array":["array_string",1234],"key_boolean":false,"key_double":12.3450,"key_number":12345,"key_object":{"age":26,"name":"huchao"},"key_string":"value_string"}
再次顧名思義,用Json::StyledWriter是格式化後的json,下面我們來看看Json::StyledWriter是怎樣格式化的。
Json::StyledWriter styled_writer;
std::cout << styled_writer.write(root) << std::endl;
輸出結果爲:
{"key_array" : [ "array_string", 1234 ], "key_boolean" : false,"key_double" : 12.3450, "key_number" : 12345, "key_object" : { "age" : 26, "name" : "huchao"}, "key_string" : "value_string" }
3、Reader
Json::Reader是用於讀取的,說的確切點,是用於將字符串轉換爲Json::Value對象的,下面我們來看個簡單的例子。
Json::Reader reader;
json::Value json_object;
const char* json_document = "{\"age\" : 26,\"name\" : \"huchao\"}";
if (!reader.parse(json_document, json_object))
return 0;
std::cout << json_object["name"] << std::endl;
std::cout << json_object["age"] << std::endl;
輸出結果爲:
"huchao"
26
可見,上述代碼已經解析出了json字符串。
--------------------------------------
[1]:使用第三方源碼最簡單的方法是直接將文件加入工程,但這樣不利於源碼、軟件產品管理,對於一般軟件開發來說,不建議使用。
[2]:如果真需要編譯成動態鏈接庫、靜態導入庫的話,可以使用VS新建一個工程屬性,然後在Project --> Properties中進行相應的設置即可。
轉載地址:http://hi.baidu.com/awz_tiger/blog/item/d165970b4ca967fc36d122a4.html
另一個比較有用的地址爲:http://blog.csdn.net/vagrxie/article/details/5754179
參考資料: http://blog.csdn.net/chen19870707/article/details/39646173
http://www.cppblog.com/wanghaiguang/archive/2013/12/26/205020.html
http://bbs.csdn.net/topics/370006412