我想要一种尽可能自动地将对象序列化和反序列化为JSON的方法.
序列化:
对我来说,理想的方法是,如果我在实例中调用JSONSerialize(),它将返回一个带有JSON对象的字符串,该对象具有该对象的所有公共属性"name_of_property": "value".对于那些作为原语的值,它很简单,对于它应该尝试在每个JSONSerialize()或ToString()上调用的对象来递归序列化所有公共属性.对于集合,它也应该正常运行(只需矢量/数组就可以了).
反序列化:只需创建给定对象的实例(让我们说一只狗)并调用JSONDeserialize(json_string),这应该填充所有公共属性,在属性不是基元或需要的集合的情况下创建所需的对象.
一个例子应该像这样运行:
Dog *d1 = new Dog();
d1->name = "myDog";
string serialized = d1->JSONSerialize();
Dog *d2 = new Dog();
d2->JSONDeserialize(serialized);
std::cout << d2->name; // This will print "myDog"
Run Code Online (Sandbox Code Playgroud)
或者像那样:
Dog *d1 = new Dog();
d1->name = "myDog";
string serialized = JSONSerializer.Serialize(d1);
Dog *d2 = JSONSerializer.Deserialize(serialized, Dog);
std::cout << d2->name; // This will print "myDog"
Run Code Online (Sandbox Code Playgroud)
我该如何轻松地将其拉下来?
Gui*_*cot 76
C++中没有反映.真正.但是如果编译器无法为您提供所需的元数据,您可以自己提供.
让我们从创建属性结构开始:
template<typename Class, typename T>
struct PropertyImpl {
constexpr PropertyImpl(T Class::*aMember, const char* aName) : member{aMember}, name{aName} {}
using Type = T;
T Class::*member;
const char* name;
};
template<typename Class, typename T>
constexpr auto property(T Class::*member, const char* name) {
return PropertyImpl<Class, T>{member, name};
}
Run Code Online (Sandbox Code Playgroud)
当然,您也可以property使用setter和getter而不是指向成员的指针,并且可能只读取您要序列化的计算值的属性.如果使用C++ 17,则可以进一步扩展它以创建一个与lambdas一起使用的属性.
好的,现在我们有了编译时内省系统的构建块.
现在在您的课程中Dog,添加您的元数据:
struct Dog {
std::string barkType;
std::string color;
int weight = 0;
bool operator==(const Dog& rhs) const {
return std::tie(barkType, color, weight) == std::tie(rhs.barkType, rhs.color, rhs.weight);
}
constexpr static auto properties = std::make_tuple(
property(&Dog::barkType, "barkType"),
property(&Dog::color, "color"),
property(&Dog::weight, "weight")
);
};
Run Code Online (Sandbox Code Playgroud)
我们需要迭代该列表.要迭代元组,有很多方法,但我首选的是:
template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F&& f) {
using unpack_t = int[];
(void)unpack_t{(static_cast<void>(f(std::integral_constant<T, S>{})), 0)..., 0};
}
Run Code Online (Sandbox Code Playgroud)
如果编译器中有C++ 17折表达式,那么for_sequence可以简化为:
template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F&& f) {
(static_cast<void>(f(std::integral_constant<T, S>{})), ...);
}
Run Code Online (Sandbox Code Playgroud)
这将为整数序列中的每个常量调用一个函数.
如果此方法不起作用或给编译器带来麻烦,则可以始终使用数组扩展技巧.
现在您已拥有所需的元数据和工具,您可以遍历属性以反序列化:
// unserialize function
template<typename T>
T fromJson(const Json::Value& data) {
T object;
// We first get the number of properties
constexpr auto nbProperties = std::tuple_size<decltype(T::properties)>::value;
// We iterate on the index sequence of size `nbProperties`
for_sequence(std::make_index_sequence<nbProperties>{}, [&](auto i) {
// get the property
constexpr auto property = std::get<i>(T::properties);
// get the type of the property
using Type = typename decltype(property)::Type;
// set the value to the member
// you can also replace `asAny` by `fromJson` to recursively serialize
object.*(property.member) = Json::asAny<Type>(data[property.name]);
});
return object;
}
Run Code Online (Sandbox Code Playgroud)
并序列化:
template<typename T>
Json::Value toJson(const T& object) {
Json::Value data;
// We first get the number of properties
constexpr auto nbProperties = std::tuple_size<decltype(T::properties)>::value;
// We iterate on the index sequence of size `nbProperties`
for_sequence(std::make_index_sequence<nbProperties>{}, [&](auto i) {
// get the property
constexpr auto property = std::get<i>(T::properties);
// set the value to the member
data[property.name] = object.*(property.member);
});
return data;
}
Run Code Online (Sandbox Code Playgroud)
如果您想要递归序列化和反序列化,可以替换asAny为fromJson.
现在您可以使用以下功能:
Dog dog;
dog.color = "green";
dog.barkType = "whaf";
dog.weight = 30;
Json::Value jsonDog = toJson(dog); // produces {"color":"green", "barkType":"whaf", "weight": 30}
auto dog2 = fromJson<Dog>(jsonDog);
std::cout << std::boolalpha << (dog == dog2) << std::endl; // pass the test, both dog are equal!
Run Code Online (Sandbox Code Playgroud)
完成!不需要运行时反射,只需要一些C++ 14的优点!
这段代码可以从一些改进中受益,当然可以在C++ 11中使用一些辅助功能.
请注意,需要编写该asAny函数.它只是一个函数,它接受Json::Value并调用正确的as...函数或其他函数fromJson.
这是一个完整的,有效的例子,由这个答案的各个代码片段组成.随意使用它.
正如评论中提到的,此代码不适用于msvc.如果你想要兼容的代码,请参考这个问题:指向成员的指针:在GCC中工作但在VS2015中不工作
小智 9
为此,您需要使用C/C++语言进行反射,而这种反映并不存在.您需要一些元数据来描述类的结构(成员,继承的基类).目前,C/C++编译器不会在构建的二进制文件中自动提供该信息.
我有同样的想法,我使用GCC XML项目来获取这些信息.它输出描述类结构的XML数据.我已经构建了一个项目,我在这个页面中解释了一些关键点:
序列化很简单,但我们必须处理使用已分配缓冲区的复杂数据结构实现(例如std :: string,std :: map).反序列化更复杂,您需要使用其所有成员重建对象,以及对vtable的引用......这是一个痛苦的实现.
例如,您可以像这样序列化:
// Random class initialization
com::class1* aObject = new com::class1();
for (int i=0; i<10; i++){
aObject->setData(i,i);
}
aObject->pdata = new char[7];
for (int i=0; i<7; i++){
aObject->pdata[i] = 7-i;
}
// dictionary initialization
cjson::dictionary aDict("./data/dictionary.xml");
// json transformation
std::string aJson = aDict.toJson<com::class1>(aObject);
// print encoded class
cout << aJson << std::endl ;
Run Code Online (Sandbox Code Playgroud)
要反序列化数据,它的工作方式如下:
// decode the object
com::class1* aDecodedObject = aDict.fromJson<com::class1>(aJson);
// modify data
aDecodedObject->setData(4,22);
// json transformation
aJson = aDict.toJson<com::class1>(aDecodedObject);
// print encoded class
cout << aJson << std::endl ;
Run Code Online (Sandbox Code Playgroud)
输出映象:
>:~/cjson$ ./main
{"_index":54,"_inner": {"_ident":"test","pi":3.141593},"_name":"first","com::class0::_type":"type","com::class0::data":[0,1,2,3,4,5,6,7,8,9],"com::classb::_ref":"ref","com::classm1::_type":"typem1","com::classm1::pdata":[7,6,5,4,3,2,1]}
{"_index":54,"_inner":{"_ident":"test","pi":3.141593},"_name":"first","com::class0::_type":"type","com::class0::data":[0,1,2,3,22,5,6,7,8,9],"com::classb::_ref":"ref","com::classm1::_type":"typem1","com::classm1::pdata":[7,6,5,4,3,2,1]}
>:~/cjson$
Run Code Online (Sandbox Code Playgroud)
通常这些实现依赖于编译器(例如ABI规范),并且需要外部描述才能工作(GCCXML输出),这些实现并不容易集成到项目中.
有什么比这更容易存在吗?谢谢 :))
C++不会在编译代码中存储类成员名称,也无法发现(在运行时)哪些成员(变量/方法)类包含.换句话说,您无法遍历结构的成员.因为没有这样的机制,您将无法为每个对象自动创建"JSONserialize".
但是,您可以使用任何json库来序列化对象,但是您必须自己为每个类编写序列化/反序列化代码.要么是这样,要么你必须创建类似于QVariantMap的可序列化类,它将被用来代替所有可序列化对象的结构.
换句话说,如果您可以为所有可序列化对象使用特定类型(或者为每个类自己编写序列化例程),那么就可以完成.但是,如果您想自动序列化每个可能的类,您应该忘记它.如果此功能对您很重要,请尝试其他语言.
使用快速类型,您可以从 JSON 示例数据生成 C++ 序列化器和反序列化器。
例如,给定示例 JSON:
{
"breed": "Boxer",
"age": 5,
"tail_length": 6.5
}
Run Code Online (Sandbox Code Playgroud)
快速类型生成:
{
"breed": "Boxer",
"age": 5,
"tail_length": 6.5
}
Run Code Online (Sandbox Code Playgroud)
要解析 Dog JSON 数据,请包含上面的代码,安装Boost和json.hpp,然后执行:
Dog dog = nlohmann::json::parse(jsonString);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
74630 次 |
| 最近记录: |