xpack
English
- Used to convert between C++ structures and json/xml/yaml/bson/mysql/sqlite
- There are only header files, no library files need to be compiled, so there is no Makefile.
- Supports bson, depends on
libbson-1.0
, needs to be installed by yourself. Not fully tested , please refer to README for details - Supports MySQL and depends on
libmysqlclient-dev
, which needs to be installed by yourself. Not fully tested - Supports Sqlite and depends on libsqlite3, which needs to be installed by yourself. Not fully tested
- Supports yaml, depends on yaml-cpp, needs to be installed by yourself. Not fully tested
- For details, please refer to example
- Basic usage
- Container support
- FLAG
- Alias
- bit field
- inherit
- enumerate
- Custom codec
- union
- indefinite type
- array
- Third-party classes and structures
- Format indent
- XML array
- CDATA
- Qt support
- MySQL
- Important note
Basic usage
- The XPACK macro is used after the structure to contain each variable. XPACK also requires a letter. Please refer to FLAG for the meanings of different letters.
- Use xpack::json::encode to convert the structure to json
- Use xpack::json::decode to convert json to structure
# 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 ;
}
Container support
Currently the following containers (std) are supported
- vector
- set
- list
- map<string, T>
- map<integer, T> // JSON only, XML is not supported
- unordered_map<string, T> (requires C++11 support)
- shared_ptr (requires C++11 support)
FLAG
In the macro XPACK, variables need to be included with letters, such as XPACK(O(a,b)). XPACK can contain multiple letters, and each letter can contain multiple variables. Currently supported letters are:
- X. The format is X(F(flag1, flag2...), member1, member2,...) F contains various FLAGs, currently supported are:
- 0 no FLAG
- OE omitempty, when encoding, if the variable is 0 or an empty string or false, the corresponding key information will not be generated.
- EN empty as null, used for encoding of json. OE is a field that does not generate empty directly, while EN generates a null.
- M mandatory, when decoding, if this field does not exist, an exception will be thrown, used for some id fields.
- ATTR attribute, when encoding xml, put the value into the attribute.
- SL single line, when json encodes, for arrays, put them in one line
- C. The format is C(customcodec, F(flag1,flags...), member1, member2,...) for custom coding and decoding functions. For details, please refer to Custom Codec
- O. Equivalent to X(F(0), ...) without any FLAG.
- M. Equivalent to X(F(M),...) indicating that these fields must exist.
- A. Alias, A(member1, alias1, member2, alias2...), used when the variable and key names are different
- AF. Alias with FLAG, AF(F(flag1, flag2,...), member1, alias1, member2, alias2...)
- B. Bitfield, B(F(flag1, flag2, ...), member1, member2, ...) bitfield does not support aliases
- I. Inheritance, I(baseclass1, baseclass2....), put the parent class in it
- E. enumerate:
- If the compiler supports C++11, there is no need to use E, and the enumeration can be placed in X/O/M/A.
- Otherwise, the enumeration can only be placed in E, and aliases are not supported.
Alias
- Used for scenarios where the variable name and key name are inconsistent
- The format is A(variable, alias....) or AF(F(flags), variable, alias....), and the alias format is "xt:n" format
- x represents the global alias, t represents the type (currently supports json, xml, and bson), and n represents the alias under the type.
- There is no need for a global alias. For example
json:_id
is legal. - There is no need for type aliases. For example,
_id
is legal. - If there is a type alias, use the type alias first. Otherwise, use the global alias. If there is no type alias, use the variable name.
# 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 ;
}
bit field
- Use "B" to include bitfield variables. Bitfields do not support aliases.
# 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 ;
}
inherit
- Use "I" to include the parent class. If you need to use the variables of the parent class, include them. If you don't need them, you don't need to include them.
- The parent class of the parent class also needs to be included, such as class Base; class Base1:public Base; class Base2:public Base1; then I(Base1, Base) is needed in Base2
- The parent class also needs to define the XPACK/XPACK_OUT macro.
# 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 " : " [email protected] " , " version " :2019, " id " :123, " name " : " Pony " } " ;
xpack::json::decode (json, t);
cout<<t. mail <<endl;
cout<<t. version <<endl;
return 0 ;
}
enumerate
- If the compiler supports C++11, the enumeration has the same name as an ordinary variable and can be placed in X/O/M/A.
- Otherwise, it needs to be placed in E, the format is 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 ;
}
Custom codec
Application scenarios
- Some basic types want to be encoded in a custom way, such as using strings to encode integers/floating point numbers.
- Some types may not want to be encoded one by one according to the structure variables. For example, if a time structure is defined:
struct Time {
long ts; // unix timestamp
};
We do not want to encode it into the format {"ts":1218196800}, but we want to encode it into the format "2008-08-08 20:00:00".
There are two ways here:
- Using xtype, you can refer to the example
- Use C to include variables that require custom encoding and decoding (hereinafter referred to as the C method), you can refer to the example
Both methods essentially implement encode/decode by themselves, but there are the following differences:
- xtype is at the type level, that is, once a type is encapsulated with xtype, the customized encode/decode will take effect on this type. xtype cannot be used on basic types (int/string, etc.)
- The C method can support basic types (int/string, etc.) and non-basic types, but only works on variables contained in C, such as int a; int b; O(a), C(custome_int, F(0), b) ;Then a still uses the default codec, and b uses the custom codec.
- xtype takes precedence over the XPACK macro, that is, if xtype is defined, the encode/decode of xtype will be used first.
- The C method takes precedence over xtype, that is, variables contained in C will definitely use the encoding and decoding methods specified in C.
Using these two features, you can achieve some more flexible encoding and decoding controls. For example, this example implements a function of encoding based on variable conditions. If Sub.type==1, encode seq1, otherwise encode seq2. __x_pack_decode
and __x_pack_encode
are XPACK The decode/encode functions added by the macro to the structure, custom encoding and decoding functions can call xpack's default encoding and decoding functions through these functions.
union
You can use custom codecs to process unions, please refer to the example
array
- When decoding, if the number of elements exceeds the length of the array, it will be truncated.
- Char arrays are processed as if they have a terminator
# 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 " : " [email protected] " } " ;
xpack::json::decode (json, t);
cout<<t. name <<endl;
cout<<t. email <<endl;
return 0 ;
}
indefinite type
- Scenarios where schema for json is uncertain
- Use xpack::JsonData to receive this information
- You can refer to examples
- The main methods of xpack::JsonData are:
- Type. used to get type
- IsXXX series of functions. Used to determine whether it is a certain type, basically equivalent to return Type()==xxxx;
- GetXXX series functions. Used to extract values.
- Overload bool. Used to determine whether it is a legal JsonData.
- Size. Used to determine the number of elements in an array type
-
operator [](size_t index)
is used to get the index element of the array (starting from 0) -
operator [](const char *key)
is used to get elements of type Object based on key - Begin. Used to traverse the elements of Object, taking the first one.
- Next. Use it with Begin to get the next element.
- Key. Configure Begin and Next to use, and obtain the Key when traversing
Third-party classes and structures
- Use XPACK_OUT instead of XPACK to include variables
- XPACK_OUT must be defined in the global namespace
# 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 ;
}
Format indent
- The json/xml generated by encode by default is not indented and is suitable for program use. If it is read by people, it can be indented.
- The last two parameters of encode control
- indentCount represents the number of characters for indentation, <0 represents no indentation, 0 represents a newline but no indentation.
- indentChar represents the indented character, using spaces or tabs
XML array
- Arrays use variable names as element labels by default, such as "ids":[1,2,3], and the corresponding xml is:
< ids >
< ids >1</ ids >
< ids >2</ ids >
< ids >3</ ids >
</ ids >
- You can use aliases to control the labels of the elements of the array, such as A(ids,"xml:ids,vl@id"), vl is followed by @xx, xx is the label of the array, and the generated result is:
< ids >
< id >1</ id >
< id >2</ id >
< id >3</ id >
</ ids >
- If you want the array to be expanded directly instead of wrapping it with a layer outside, you can use the alias plus "sbs" flag to achieve this, such as A(ids, "xml:ids,sbs"). Note that the sbs tag can only be used for arrays. Others Local use may crash
< ids >1</ ids >
< ids >2</ ids >
< ids >3</ ids >
CDATA
- For the CDATA type, you need to use the "cdata" flag to implement, such as A(data, "xml:data,cdata")
- cdata can only be received using std::string
- If the xml corresponding to the variable is not a CDATA structure, it will be processed as a normal string. For example,
<data>hello</data>
can also be parsed successfully.
Qt support
- Modify config.h and enable the XPACK_SUPPORT_QT macro (or enable it in the compilation option)
- Currently supports QString/QMap/QList/QVector
MySQL
- Currently only decode is supported, and encode is not supported yet.
- Not fully tested, use with caution
- Currently supported types are:
- string. Simple test.
- Integer type. Simple test.
- Floating point type. Not tested.
- Use integer type (such as time_t) to receive TIME/DATETIME/TIMESTAMP. Not tested.
- Custom type conversion, is_xpack_mysql_xtype, similar to xtype. Not tested.
- There are two APIs (xpack::mysql::):
-
static void decode(MYSQL_RES *result, T &val)
- Used to convert MYSQL_RES into a structure or vector<>. If it is non-vector, only the first row will be converted.
-
static void decode(MYSQL_RES *result, const std::string&field, T &val)
- Used to parse a certain field, used in scenarios where you only want to get the content of a certain field, such as select id from mytable where name = lilei, and just want to get the id information. val supports vector
Important note
- Try not to start the variable name with __x_pack, otherwise it may conflict with the library.
- vc6 is not supported.
- msvc has not done many tests, only simple tests in 2019.
- The serialization and deserialization of json uses rapidjson.
- The deserialization of xml uses rapidxml
- The serialization of xml was written by myself without referring to RFC, so it may be different from the standard.
- If you have any questions, you can join QQ group 878041110