用于json转结构体,结构体转json。使用方法很简单,包含头文件,结构体内加个宏即可。

代码github地址(原来是叫x2struct,由于一些很少用的特性弄的太复杂难以扩展,所以弄了个新的工程)

gitee地址

  1.  用于在C++结构体和json/xml/bson之间互相转换
  2.  json/xml 只需要头文件, 无需编译库文件
  3. 使用简单,只需要在结构体内添加一个宏把结构体变量放进去即可。
  4.  以下例子全部以json为例,xml/bson接口和json类似,具体参考xml.h

目录

基本用法

容器支持

FLAG

别名

继承

位域

枚举

自定义编解码

char数组

第三方类或结构体

格式化缩进

XML数组

Qt支持

说明


基本用法

  1. 引用头文件。如果只用json,直接引用json.h即可。
  2. 添加宏定义XPACK在结构体结尾, 里面用"O"包含所有变量
  3. 用xpack::json::encode将结构体转json
  4. 用xpack::json::decode将json转结构体
#include <iostream>
#include "xpack/json.h" // Json包含这个头文件,xml则包含xpack/xml.h

using namespace std;

struct User {
    int id;
    string  name;
    XPACK(O(id, name)); // 添加宏定义XPACK在结构体定义结尾
};

int main(int argc, char *argv[]) {
    User u;
    string data = "{\"id\":12345, \"name\":\"xpack\"}";

    xpack::json::decode(data, u);          // json转结构体
    cout<<u.id<<';'<<u.name<<endl;

    string json = xpack::json::encode(u);  // 结构体转json
    cout<<json<<endl;

    return 0;
}

 

容器支持

目前支持下列容器(std)

  • vector
  • set
  • list
  • map<string, T>
  • unordered_map<string, T> (需要C++11支持)
  • shared_ptr (需要C++11支持)

FLAG

宏XPACK里面,需要用字母将变量包含起来,比如XPACK(O(a,b)),目前支持的字母有:

  • X。格式是X(F(flag1, flag2...), member1, member2,...) F里面包含各种FLAG,目前支持的有:
    • 0 没有任何FLAG
    • OE omitempty,encode的时候,如果变量是0或者空字符串或者false,则不生成对应的key信息
    • M mandatory,decode的时候,如果这个字段不存在,则抛出异常,用于一些id字段。
    • ATTR attribute,xml encode的时候,把值放到attribute里面。
  • O。等价于X(F(0), ...) 没有任何FLAG。
  • M。等价于X(F(M),...) 表示这些字段是必须存在的。
  • A。别名,A(member1, alias1, member2, alias2...)
  • AF。带FLAG的别名,AF(F(flag1, flag2,...), member1, alias1, member2, alias2...)
  • B。位域,B(F(flag1, flag2, ...), member1, member2, ...) 位域不支持别名
  • I。继承,I(baseclass1, baseclass2....)
  • E。枚举:
    • 如果编译器支持C++11,不需要用E,枚举可以放X/O/M/A里面。
    • 否则枚举只能放E里面,不支持别名

别名

  • 用于变量名和key名不一致的场景
  • 格式是A(变量,别名....)或者AF(F(flags), 变量,别名....),别名的格式是"x t:n"的格式
    • x表示全局别名,t表示类型(目前支持json),n表示类型下的别名
    • 全局别名可以没有,比如"json:_id"是合法的
    • 类型别名可以没有,比如"_id"是合法的
    • 有类型别名优先用类型别名,否则用全局别名,都没有,则用变量名
#include <iostream>
#include "xpack/json.h"

using namespace std;

struct Test {
    long uid;
    string  name;
    XPACK(A(uid, "id"), O(name)); // "uid"的别名是"id"
};

int main(int argc, char *argv[]) {
    Test t;
    string json="{\"id\":123, \"name\":\"Pony\"}";

    xpack::json::decode(json, t); 
    cout<<t.uid<<endl;
    return 0;
}

 

继承

  • 使用"I"来包含父类
  • 所有父类都需要包含,比如class Base; class Base1:public Base; class Base2:public Base1;那么在Base2中需要I(Base1, Base)
  • 父类也需要定义XPACK/XPACK_OUT宏。
#include <iostream>
#include "xpack/json.h"

using namespace std;

struct P1 {
    string mail;
    XPACK(O(mail));
};

struct P2 {
    long version;
    XPACK(O(version));
};

struct Test:public P1, public P2 {
    long uid;
    string  name;
    XPACK(I(P1, P2), O(uid, name)); 
};

int main(int argc, char *argv[]) {
    Test t;
    string json="{\"mail\":\"pony@xpack.com\", \"version\":2019, \"id\":123, \"name\":\"Pony\"}";

    xpack::json::decode(json, t);
    cout<<t.mail<<endl;
    cout<<t.version<<endl;
    return 0;
}

位域

  • 使用"B"来包含位域变量,位域不支持别名
#include <iostream>
#include "xpack/json.h"

using namespace std;

struct Test {
    short ver:8;
    short len:8;
    string  name;
    XPACK(B(F(0), ver, len), O(name)); 
};

int main(int argc, char *argv[]) {
    Test t;
    string json="{\"ver\":4, \"len\":20, \"name\":\"IPv4\"}";

    xpack::json::decode(json, t);
    cout<<t.ver<<endl;
    cout<<t.len<<endl;
    return 0;
}

枚举

  • 如果编译器支持C++11,则枚举和普通变量名一样,放X/O/M/A里面皆可。
  • 否则需要放到E里面,格式是E(F(...), member1, member2, ...)
#include <iostream>
#include "xpack/json.h"

using namespace std;

enum Enum {
    X = 0,
    Y = 1,
    Z = 2,
};

struct Test {
    string  name;
    Enum    e;
    XPACK(O(name), E(F(0), e)); 
};

int main(int argc, char *argv[]) {
    Test t;
    string json="{\"name\":\"IPv4\", \"e\":1}";

    xpack::json::decode(json, t);
    cout<<t.name<<endl;
    cout<<t.e<<endl;
    return 0;
}

 

自定义编解码

应用场景:部分类型可能不想按结构体变量逐个编码,比如定义了一个时间结构体:

struct Time {
    long ts; //unix timestamp
};

并不希望编码成{"ts":1218196800} 这种格式,而是希望编码成"2008-08-08 20:00:00"这种格式,这个时候就可以用自定义编解码实现。

#include <iostream>
#include "xpack/xtype.h"
#include "xpack/json.h"
#include "xpack/xml.h"

using namespace std;

struct Date {
    long unix_time;
};

namespace xpack { // must define in namespace xpack

template<>
struct is_xpack_xtype<Date> {static bool const value = true;};

// implement decode
template<class OBJ>
bool xpack_xtype_decode(OBJ &obj, const char*key, Date &val, const Extend *ext) {
    std::string str;
    obj.decode(key, str, ext);
    if (str.empty()) {
        return false;
    }

#ifndef _MSC_VER
    tm ttm;

    if (0 != strptime(str.c_str(), "%Y-%m-%d %H:%M:%S", &ttm)) {
        val.unix_time = mktime(&ttm);
    } else {
        val.unix_time = 0;
    }
#else
    static int days[]={31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 32};
    struct tm ttm={0};
    sscanf_s(str.c_str(), "%d-%d-%d %d:%d:%d", &ttm.tm_year, &ttm.tm_mon, &ttm.tm_mday, &ttm.tm_hour, &ttm.tm_min, &ttm.tm_sec);
    ttm.tm_mon-=1; // mon[0-11]
    ttm.tm_year-=1900; // since 1900
    val.unix_time = mktime(&ttm);
#endif
    return true;
}

// implement encode
template<class OBJ>
bool xpack_xtype_encode(OBJ &obj, const char*key, const Date &val, const Extend *ext) {
    time_t tt = (time_t)val.unix_time;
    tm     ttm;

#ifndef _MSC_VER
    localtime_r(&tt, &ttm);
#else
    localtime_s(&ttm, &tt);
#endif

    char buf[64];
    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &ttm);
    return obj.encode(key, buf, ext);
}

}

struct User {
    int64_t id;
    string  name;
    string  mail;
    Date    d;
    User(int64_t i=0, const string& n="", const string& m="", long _d=0):id(i),name(n),mail(m){d.unix_time = _d;}
    XPACK(O(id, name, mail, d));
};

struct Group {
    string  name;
    int64_t master;
    vector<User> members;
    XPACK(O(name, master, members));
};

int main(int argc, char *argv[]) {
    Group g;
    g.name = "C++";
    g.master = 2019;
    g.members.resize(2);
    g.members[0] = User(1, "Jack", "jack@xpack.com", 1);
    g.members[1] = User(2, "Pony", "pony@xpack.com", 1609249232);

    string json = xpack::json::encode(g, 0, 2, ' ');
    cout<<json<<endl;

    string xml = xpack::xml::encode(g, "root", 0, 2, ' ');
    cout<<xml<<endl;

    Group n1;
    xpack::json::decode(json, n1);
    cout<<n1.name<<';'<<n1.members[0].name<<';'<<n1.members[0].d.unix_time<<endl;

    Group n2;
    xpack::xml::decode(xml, n2);
    cout<<n2.name<<';'<<n2.members[1].name<<';'<<n2.members[1].d.unix_time<<endl;

    return 0;
}

 

char数组

  • 缺省是不支持char数组的
  • 修改config.h,开启XPACK_SUPPORT_CHAR_ARRAY这个宏即可。也可以直接在编译选项加上这个定义。
  • 除了char,其他类型不支持数组
#include <iostream>
#include "xpack/json.h"

using namespace std;


struct Test {
    char  name[64];
    char  email[64];
    XPACK(O(name, email)); 
};

int main(int argc, char *argv[]) {
    Test t;
    string json="{\"name\":\"Pony\", \"email\":\"pony@xpack.com\"}";

    xpack::json::decode(json, t);
    cout<<t.name<<endl;
    cout<<t.email<<endl;
    return 0;
}

 

第三方类或结构体

用于结构体是第三方库定义,没法在里面定义XPACK的场景。

  • 用XPACK_OUT而非XPACK来包含变量
  • XPACK_OUT必须定义在全局命名空间
#include <sys/time.h>
#include <iostream>
#include "xpack/json.h"

using namespace std;

/*
struct timeval {
    time_t      tv_sec;
    suseconds_t tv_usec;
};
*/

// timeval is thirdparty struct
XPACK_OUT(timeval, O(tv_sec, tv_usec));

struct T {
    int  a;
    string b;
    timeval t;
    XPACK(O(a, b, t));
};


int main(int argc, char *argv[]) {
    T t;
    T r;
    t.a = 123;
    t.b = "xpack";
    t.t.tv_sec = 888;
    t.t.tv_usec = 999;
    string s = xpack::json::encode(t);
    cout<<s<<endl;
    xpack::json::decode(s, r);
    cout<<r.a<<','<<r.b<<','<<r.t.tv_sec<<','<<r.t.tv_usec<<endl;
    return 0;
}

 

格式化缩进

  • encode缺省生成的json/xml是没有缩进的,适合程序使用,如果让人读,可以进行缩进。
  • encode的最后两个参数控制
    • indentCount 表示缩进的字符数,<0表示不缩进,0则是换行但是不缩进
    • indentChar 表示缩进的字符,用空格或者制表符

XML数组

  • 数组会用"x"作为标签,比如"ids":[1,2,3],对应的xml是:
<ids>
    <x>1</x>
    <x>2</x>
    <x>3</x>
</ids>

 

Qt支持

  • 修改config.h,开启XPACK_SUPPORT_QT这个宏(或者在编译选项开启)
  • 当前支持 QString/QMap/QList/QVector

 

说明

  • 变量名尽量不要用__x_pack开头,不然可能会和库有冲突。
  • vc6不支持。
  • msvc没有做很多测试,只用2019做过简单测试。
  • json的序列化反序列化用的是rapidjson
  • xml的反序列化用的是rapidxml
  • xml的序列化是我自己写的,没有参考RFC,可能有和标准不一样的地方.
  • 有疑问可以加QQ群878041110
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐