在開發網絡應用程序時,各個模塊之間的數據通信可謂是家常便飯,為了應對這些數據通信時數據交換的要求,程序員發明了各種數據格式:采用二進制數據結構(早期 C 程序員)、采用 XML、采用SOAP(坑人的設計)、采用 URL 編碼、采用JSON格式等。客戶端與服務端交互時采用這些數據格式進行數據交換時,必然要經歷數據編碼及數據解碼的繁瑣過程。早期的二進制數據結構格式對於 C 程序員而是比較簡單的,在解碼時直接進行結構硬對齊就OK了,但對於其它語言(如 JAVA,PHP)則就麻煩很多,JAVA 語言不僅需要采用字節流一個一個對齊,而且還得要考慮到 C 結構體的數據打包填充方式;SOAP 方式看似滿足了各類編程語言數據交換的目的,但數據臃腫是一個很大的問題,我們為了傳輸幾個字節的數據則需要不得不封裝大量的 XML 標記。為了使跨語言開發的程序員從麻煩的數據編解碼中解脫出來,后來出來很多序列化/反序列化的工具庫,比較著名的象 Facebook 的 thrift,Google 的 protobuf。這些工具庫功能非常強大,但有一個問題是,這些工具庫所要求的預先定義的 schema 的親民性不夠好,增加了額外的大量學習成本。
最近由 niukey@qq.com 為 acl 庫新增了 C++ 對象序列化與反序列化庫,大大提高了程序員的編程效率及代碼准確率。acl 序列化庫實現了 C++ struct 對象與 JSON 對象之間互轉功能,使用方式非常簡單。
一、acl 序列化庫的功能特點:
1、可以直接將 C++ struct 對象轉換為 Json 對象,同時還可以將 Json 對象反序列化為 C++ struct 對象;
2、支持 C++ struct 對象中包含構造函數及析構函數;
3、C++ struct 對象中的成員支持常見基本類型(short, int, long, long long, char*)、標准 C++ 類對象;
4、C++ struct 對象中的成員支持指針類型;
5、C++ struct 對象中的成員支持常見 C++ 容器類型:std::vector、std::list、std::map,且支持容器對象的內部嵌套;
6、C++ struct 對象中的成員為基本數據類型(如:short, int, long, long long)和指針類型時,支持直接在 struct 中針對這些成員進行初始化(C++11);
7、支持在 C++ struct 中添加注釋(// 或 /**/);
8、支持 C++ struct 對象的多繼承;
9、支持在 C++ struct 對象中的多級包含,建議使用包含方式代替繼承方式;
10、支持 C++ struct 成員增加注釋項://Gson@optional 表示該成員變量為可選項,//Gson@required 表示該成員為必須項,默認為必須的。
二、使用 acl 序列化庫的限制:
1、struct 中的成員類型不能為 char 類型,主要是因為 Json 對象中不存在 char 類型;
2、struct 中的成員前不得有 const 常量限定詞,主要是在反序列化時需要對這些成員進行賦值;
3、struct 中不支持 C++ 關鍵詞:public、private、protected;
4、struct 中的成員變量名不得為:$json、$node、$obj,因為這些關鍵詞被 acl 序列化庫生成的代碼占用;
5、存在包含關系的 struct 類型定義必須在同一個文件中;
6、不支持純 C 語言的數組功能,比如,針對 int a[10],因無法確切知道數組 a 中真實存儲的數量而無法進行序列化和反序列化,因此推薦使用 std::vector<int> 代替它。
三、使用 acl 序列化庫的過程:
1、首先編譯 acl 庫:在 acl 目錄前運行:make build_one,會生成 acl 的三個基礎庫(lib_acl/lib/lib_acl.a, lib_protocol/lib/lib_protocol.a, lib_acl_cpp/liblib_acl_cpp.a),同時還會在 acl 根目錄下生成這三個庫的合集(lib_acl.a 和 lib_acl.so);
2、編譯 acl 序列化工具:進入 app/gson 目錄,運行 make,生成 gson 工具,該工具將被用於根據用戶自定義 struct 生成序列化 C++ 代碼;
3、定義自己的 struct 結構體(可以定義多個)並保存在文件中(文件后綴名為 .stub);
4、使用 gson 工具根據用戶的 .stub 文件生成序列化所需的 C++ 代碼:./gson -d path,其中 path 為保含 .stub 文件的目錄,將會生成三個文件 gson.cpp、 gson.h 和 由 .stub 轉換的 .h 頭文件;
5、將 gson.h 和由 .stub 文件轉換的 .h 頭文件包含在自己的代碼中;
6、針對用戶自定義的每一個 struct,在 gson.h 頭文件中均會提供 struct 對象的序列化和反序列化的方法,假設用戶自定義了一個結構體類型:struct user,則在由工具 gson 生成的 gson.h 文件中包含如下五個方法:
1)、acl::string gson(const user &$obj);
將用戶填充好的 user 對象 $obj 轉換為 Json 字符串;
2)、acl::json_node& gson(acl::json &$json, const user &$obj);
將用戶填充好的 user 對象 $obj 轉化為 acl::json 對象 $json 中的一個 acl::json_node 節點對象並將之返回,這樣用戶可以將這個返回的 acl::json_node& 對象引用添加於 $json 對象中;
3)、acl::json_node& gson(acl::json &$json, const user *$obj);
功能與 2)中的方法相同,只是 $obj 參數為對象指針;
4)、std::pair<bool,std::string> gson(acl::json_node &$node, user &$obj);
將一個 acl::json_node Json 節點對象轉化為用戶自定義的 struct user 對象,如果 $node 為該 Json 對象中的根節點,則該根節點可由 $json.get_root() 獲得,$obj 存儲轉換后的結果;
5)、std::pair<bool,std::string> gson(acl::json_node &$node, user *$obj);
功能與 5)中的方法相同,只是 $obj 參數為對象指針。
四、舉例:
假設自定義結構對象如下:
- // struct.stub
- #pragma once
- #include <string>
- struct user
- {
- std::string name;
- std::string domain;
- int age;
- bool male;
- };
應用操作 user 對象的過程如下:
- // main.cpp
- #include "stdafx.h"
- #include <list>
- #include <vector>
- #include <map>
- #include <stdio.h>
- #include <iostream>
- #include <time.h>
- #include "struct.h" // 由 gson 工具根據 struct.stub 轉換而成
- #include "gson.h" // 由 gson 工具根據 struct.stub 生成
- // 序列化過程
- static void serialize(void)
- {
- user u;
- u.name = "zsxxsz";
- u.domain = "263.net";
- u.age = 11;
- u.male = true;
- acl::json json;
- // 將 user 對象轉換為 json 對象
- acl::json_node& node = acl::gson(json, u);
- printf("serialize:\r\n");
- printf("json: %s\r\n", node.to_string().c_str());
- printf("\r\n");
- }
- // 反序列化過程
- static void deserialize(void)
- {
- const char *s = "{\"name\": \"zsxxsz\", \"domain\": \"263.net\", \"age\": 11, \"male\": true}";
- printf("deserialize:\r\n");
- acl::json json;
- json.update(s);
- user u;
- // 將 json 對象轉換為 user 對象
- std::pair<bool, std::string> ret = acl::gson(json.get_root(), u);
- // 如果轉換失敗,則打印轉換失敗原因
- if (ret.first == false)
- printf("error: %s\r\n", ret.second.c_str());
- else
- printf("name: %s, domain: %s, age: %d, male: %s\r\n",
- u.name.c_str(), u.domain.c_str(), u.age,
- u.male ? "yes" : "no");
- }
- int main(void)
- {
- serialize();
- deserialize();
- return 0;
- }
五、參考:
acl github:https://github.com/acl-dev/acl
acl osc:https://git.oschina.net/acl-dev/acl/tree/master
更多 C++ 序列化的例子:https://github.com/zhengshuxin/acl/tree/master/app/gson/test
acl 編譯與使用:http://zsxxsz.iteye.com/blog/1506554