当前位置: 首页 > 知识库问答 >
问题:

C JSON序列化

颛孙晗昱
2023-03-14

我想要一种将对象序列化和反序列化为JSON的方法,尽可能自动。

Serialize:对我来说,理想的方式是,如果我调用一个实例JSONSerialize(),它会返回一个带有JSON对象的字符串,该对象的所有公共属性都是< code >“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"

或者像这样:

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"

我怎样才能轻松地完成这件事?

共有3个答案

喻增
2023-03-14

有没有像这样简单的东西存在??谢谢:))

C 不将类成员名称存储在已编译的代码中,并且无法(在运行时)发现类包含哪些成员(变量/方法)。换句话说,您不能循环访问结构的成员。由于没有这样的机制,因此无法为每个对象自动创建“JSONserialize”。

但是,您可以使用任何 json 库来序列化对象,但您必须自己为每个类编写序列化/反序列化代码。要么这样,要么你必须创建类似于QVariantMap的可序列化类,这些类将用于而不是所有可序列化对象的结构。

换言之,如果您对所有可序列化对象使用特定类型(或为每个类自己编写序列化例程)感到满意,就可以这样做。然而,如果你想自动序列化每个可能的类,你应该忘记它。如果此功能对您很重要,请尝试其他语言。

步骏
2023-03-14

为此,你需要C/C中的反射,这是不存在的。你需要一些元数据来描述你的类的结构(成员,继承的基类)。目前,C/C编译器不会在构建的二进制文件中自动提供这些信息。

我也有同样的想法,我使用GCC XML项目来获取此信息。它输出描述类结构的 XML 数据。我已经建立了一个项目,我正在解释这个页面中的一些关键点:

序列化很容易,但我们必须处理复杂的数据结构实现(例如,std::string,std:::map),这些实现与分配的缓冲区有关。反序列化更复杂,您需要重建对象及其所有成员,再加上对vtables的引用……这是一个痛苦的实现。

例如,您可以像这样序列化:

    // 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 ;

要反序列化数据,它的工作原理如下:

    // 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 ;

盎司:

>:~/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$ 

通常这些实现依赖于编译器(例如ABI规范),并且需要外部描述才能工作(GCCXML输出),因此不容易集成到项目中。

麹高义
2023-03-14

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};
}

当然,您也可以有一个<code>属性</code>,它接受一个setter和getter,而不是指向成员的指针,还可以是您想要序列化的计算值的只读属性。如果您使用C17,您可以进一步扩展它,使其成为一个可用于lambdas的属性。

好的,现在我们有了编译时自省系统的构建块。

现在在您的类< code>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")
    );
};

我们需要对该列表进行迭代。要迭代元组,有很多方法,但我最喜欢的一种是:

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};
}

如果编译器中有C 17折叠表达式,那么< code>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>{})), ...);
}

这将为整数序列中的每个常量调用一个函数。

如果这个方法不起作用或者给你的编译器带来麻烦,你总是可以使用数组扩展的技巧。

现在您已经有了所需的元数据和工具,您可以遍历属性来取消序列化:

// 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;
}

对于序列化:

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;
}

如果您想要递归序列化和反序列化,您可以将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!

做!无需运行时反射,只需一些C 14的优点!

这段代码可以从一些改进中受益,当然可以通过一些调整与C 11一起使用。

请注意,您需要编写< code>asAny函数。它只是一个函数,接受一个< code>Json::Value并调用正确的< code >作为...函数,或者另一个< code>fromJson。

下面是一个完整的工作示例,该示例由此答案的各种代码片段组成。随意使用它。

正如评论中提到的,这段代码不能在msvc上运行。如果您想要一个兼容的代码,请参考这个问题:指向成员的指针:在GCC中有效,但在VS2015中无效

 类似资料:
  • 所有包 | 方法 包 system.web.helpers 继承 class CJSON 源自 1.0 版本 $Id: CJSON.php 3204 2011-05-05 21:36:32Z alexander.makarow $ 源码 framework/web/helpers/CJSON.phpCJSON用于转换PHP数据到JSON格式基从JSON格式转换为PHP数据。 公共方法 隐藏继承方法

  • python-cjson 是一个适用于 Python 的快速 JSON 编码器/解码器。 JSON代表JavaScript Object Notation,它是一种基于文本的轻量级数据交换格式,易于人类读取/写入以及易于机器解析/生成。JSON完全独立于语言,并且在大多数编程语言中都有多种实现,使其成为数据交换和存储的理想选择。 该模块是用 C 编写的,与其他直接用python编写的python

  • Lua CJSON 为 Lua 语言提供高性能的 JSON 解析器和编码器,其性能比纯 Lua 库要高 10 到 20 倍。Lua CJSON 完全支持 UTF-8 ,无需依赖其他非 Lua/LuaJIT 的相关包。

  • php-cjson 是快速的 JSON 解析和生成的 PHP 库,基于 cjson 底层 C 库开发。 安装 $/path/to/phpize$./configure --with-php-config=/path/to/php-config$make && make install 示例 encode $arr = array(    1,    "string",    array("key"

  • cJSON-plus基于cJSON的功能扩展库。主要适用于非强交互场景,如终端操作,要使用到cJSON处理json数据。如果你有协议数据模板,使用该库可以减少很多业务逻辑的设计,减少代码的冗余。 cJSON-plus里面包含了一些基于cJSON而实现的函数。 例如: 向前添加键值对。 修改指定域的字符串。 遍历cJSON对象。 其他格式化输出对象。(支持列表和树形等) 遍历修改所有项功能。 修改指

  • Lua CJSON is a Lua C module that provides fast JSON parsing and encoding support for Lua. Project homepage: http://www.kyne.com.au/~mark/software/lua-cjson.php OpenResty includes its own fork of this

  • 我正在尝试使用kryo序列化和反序列化到二进制。我想我已经完成了序列化,但似乎无法反序列化。下面是我正在处理的代码,但最终我想存储一个字节[],然后再次读取它。文档只显示了如何使用文件。

  • 问题内容: 我尝试过在Java和Android之间实现跨平台序列化。我使用了Serializable,并将我的代码在Android中与台式机Java放在同一软件包中。 来源:java-desktop序列化 资料来源:Android-反序列化 学生是一类,实现了Serializable。在桌面上,我将学生实例序列化为“ thestudent.dat”。我将此文件放在Android设备上的SD卡上,并