【问题标题】:Is there versioning capability in MsgpackMsgpack 中是否有版本控制功能
【发布时间】:2018-02-01 10:14:03
【问题描述】:

我正在评估 Msgpack(C++) 作为我当前项目中的序列化库。它似乎满足了我的大部分需求,除了一个,我在网上没有找到太多关于它的信息。 Msgpack 是否支持读取我将要序列化的不同版本的数据结构?

例如,我序列化如下结构:

struct foo {
  int a;
  float b;
};

后来上面的结构演变成:

struct foo {
  int a;
  float b;
  std::string c;
};

是否可以使用 Msgpack 将先前序列化的结构读入新的结构? Boost 库通过在结构中添加 VERSION 元数据来处理它。

【问题讨论】:

标签: c++ serialization msgpack


【解决方案1】:

是的,您可以这样做。如果你打包foo_v1然后解包,然后将其转换为foo_v2ab都填充了打包的值。

#include <msgpack.hpp>
#include <cassert>
#include <iostream>
#include <sstream>

struct foo_v1 {
    int a;
    float b;
    MSGPACK_DEFINE(a, b); // pack as ARRAY, order is important
};

struct foo_v2 {
    int a;
    float b;
    std::string c;
    MSGPACK_DEFINE(a, b, c); // pack as ARRAY, order is important
};

int main() {
    foo_v1 v1 { 123, 45.67 };
    std::stringstream ss;
    msgpack::pack(ss, v1);

    auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
    auto v2 = oh.get().as<foo_v2>();
    std::cout << "a: " << v2.a << std::endl;
    std::cout << "b: " << v2.b << std::endl;
    std::cout << "c: " << v2.c << std::endl;
}

运行演示:https://wandbox.org/permlink/91wRtVdJJCC5IEDx

同样的,如果你打包foo_v2然后解包,然后将其转换为foo_v1ab填充了打包的值,c被切片(忽略)。

#include <msgpack.hpp>
#include <cassert>
#include <iostream>
#include <sstream>

struct foo_v1 {
    int a;
    float b;
    MSGPACK_DEFINE(a, b); // pack as ARRAY, order is important
};

struct foo_v2 {
    int a;
    float b;
    std::string c;
    MSGPACK_DEFINE(a, b, c); // pack as ARRAY, order is important
};

int main() {
    foo_v2 v2 { 123, 45.67, "hello" };
    std::stringstream ss;
    msgpack::pack(ss, v2);

    auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
    auto v1 = oh.get().as<foo_v1>();
    std::cout << "a: " << v1.a << std::endl;
    std::cout << "b: " << v1.b << std::endl;
}

运行演示:https://wandbox.org/permlink/mxmSkVHebZFiOM1q

这些示例使用MSGPACK_DEFINE 宏。见https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_adaptor#defining-custom-adaptors。 默认情况下,它用于打包/转换为 ARRAY。所以顺序很重要。 如果您使用MSGPACK_DEFINE_MAP,则用户类将打包/转换为 MAP。 MAP的key默认为变量名。您可以使用MSGPACK_NVP 更改它,请参阅https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_adaptor#since-210MAP 的值是成员变量的值。 MAPARRAY 更灵活但效率低下。

如果您使用MSGPACK_DEFINE_MAP,则无需关心订单。

#include <msgpack.hpp>
#include <cassert>
#include <iostream>
#include <sstream>

struct foo_v1 {
    int a;
    float b;
    MSGPACK_DEFINE_MAP(a, b); // pack as MAP
};

struct foo_v2 {
    int a;
    std::string c;
    float b;
    MSGPACK_DEFINE_MAP(a, c, b); // pack as MAP, c is at the middle position
};

int main() {
    foo_v2 v2 { 123, "hello", 45.67,  };
    std::stringstream ss;
    msgpack::pack(ss, v2);

    auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
    auto v1 = oh.get().as<foo_v1>();
    std::cout << "a: " << v1.a << std::endl;
    std::cout << "b: " << v1.b << std::endl;
}

运行演示:https://wandbox.org/permlink/ozihpwXMJRpOhzT4

这里是更复杂的例子: https://github.com/msgpack/msgpack-c/blob/master/example/cpp03/map_based_versionup.cpp

运行演示:https://wandbox.org/permlink/IUp94Tc4MiT4kU07

【讨论】:

  • 非常感谢您提供的示例!这回答了我的问题。