Visitor interface
msgpack-c提供解包API。它们将msgpack格式的字节转换为msgpack::object。 然后,将msgpack::object用于多种用途。它类似于XML的DOM(文档对象模型)API。visitor接口为msgpack格式的字节提供了更直接的访问,比如SAXAPI for XML。 您可以定义自己的访问者并将其传递给parse()API。在分析过程中,遍历数据的迭代器。当迭代器遇到msgpack元素时,将调用与该元素对应的访问者成员函数。
Visitor concept
struct visitor {
bool visit_nil();
bool visit_boolean(bool v);
bool visit_positive_integer(uint64_t v);
bool visit_negative_integer(int64_t v);
bool visit_float32(float v);
bool visit_float64(double v);
bool visit_str(const char* v, uint32_t size);
bool visit_bin(const char* v, uint32_t size);
bool visit_ext(const char* v, uint32_t size);
bool start_array(uint32_t num_elements);
bool start_array_item();
bool end_array_item();
bool end_array();
bool start_map(uint32_t num_kv_pairs);
bool start_map_key();
bool end_map_key();
bool start_map_value();
bool end_map_value();
bool end_map();
void parse_error(size_t parsed_offset, size_t error_offset);
void insufficient_bytes(size_t parsed_offset, size_t error_offset);
bool referenced() const; // Only when you want to use the visitor with a parser
};
visitor必须实现上述所有成员功能。如果只想实现几个成员函数,那么可以继承预定义的 msgpack::null_visitor.
struct null_visitor {
bool visit_nil() {
return true;
}
bool visit_boolean(bool /*v*/) {
return true;
}
bool visit_positive_integer(uint64_t /*v*/) {
return true;
}
bool visit_negative_integer(int64_t /*v*/) {
return true;
}
bool visit_float32(float /*v*/) {
return true;
}
bool visit_float64(double /*v*/) {
return true;
}
bool visit_str(const char* /*v*/, uint32_t /*size*/) {
return true;
}
bool visit_bin(const char* /*v*/, uint32_t /*size*/) {
return true;
}
bool visit_ext(const char* /*v*/, uint32_t /*size*/) {
return true;
}
bool start_array(uint32_t /*num_elements*/) {
return true;
}
bool start_array_item() {
return true;
}
bool end_array_item() {
return true;
}
bool end_array() {
return true;
}
bool start_map(uint32_t /*num_kv_pairs*/) {
return true;
}
bool start_map_key() {
return true;
}
bool end_map_key() {
return true;
}
bool start_map_value() {
return true;
}
bool end_map_value() {
return true;
}
bool end_map() {
return true;
}
void parse_error(size_t /*parsed_offset*/, size_t /*error_offset*/) {
}
void insufficient_bytes(size_t /*parsed_offset*/, size_t /*error_offset*/) {
}
};
除错误通知函数外,所有成员函数都具有布尔返回类型。一般情况下,返回true代表要继续解包,false则表示停止解包。
名为visit_xxx()的成员函数是叶元素的访问者。在数组开始/结束时调用start_array()/end_array()。在每个数组项之前/之后调用start_array_item()/end_array_item()。
当map开始/结束时会调用start_map()/end_map()。在每个映射键之前/之后调用start_map_key()/end_map_key()。在每个映射值之前/之后调用start_map_value()/end_map_value()。
发生解析错误时,将调用parse_error()。当缓冲区包含的字节不足,无法生成完整的msgpack时,将调用uncipled_bytes()。
Example
下面是一个将msgpack格式的字节转换为类似JSON的结构的示例(我省略了字符串转义,因为这不是我想演示的一点):
struct json_like_visitor : msgpack::v2::null_visitor {
json_like_visitor(std::string& s):m_s(s) {}
bool visit_nil() {
m_s += "null";
return true;
}
bool visit_boolean(bool v) {
if (v) m_s += "true";
else m_s += "false";
return true;
}
bool visit_positive_integer(uint64_t v) {
std::stringstream ss;
ss << v;
m_s += ss.str();
return true;
}
bool visit_negative_integer(int64_t v) {
std::stringstream ss;
ss << v;
m_s += ss.str();
return true;
}
bool visit_str(const char* v, uint32_t size) {
m_s += '"' + std::string(v, size) + '"';
return true;
}
bool start_array(uint32_t /*num_elements*/) {
m_s += "[";
return true;
}
bool end_array_item() {
m_s += ",";
return true;
}
bool end_array() {
m_s.erase(m_s.size() - 1, 1); // remove the last ','
m_s += "]";
return true;
}
bool start_map(uint32_t /*num_kv_pairs*/) {
m_s += "{";
return true;
}
bool end_map_key() {
m_s += ":";
return true;
}
bool end_map_value() {
m_s += ",";
return true;
}
bool end_map() {
m_s.erase(m_s.size() - 1, 1); // remove the last ','
m_s += "}";
return true;
}
void parse_error(size_t /*parsed_offset*/, size_t /*error_offset*/) {
// report error.
}
void insufficient_bytes(size_t /*parsed_offset*/, size_t /*error_offset*/) {
// report error.
}
std::string& m_s;
};
int main()
{
std::stringstream ss;
msgpack::packer<std::stringstream> p(ss);
p.pack_map(1);
p.pack("key");
p.pack_array(3);
p.pack(42);
p.pack_nil();
p.pack(true);
std::string json_like;
json_like_visitor v(json_like);
std::size_t off = 0;
std::string const& str = ss.str();
bool ret = msgpack::v2::parse(str.data(), str.size(), off, v);
assert(ret);
assert("{\"key\":[42,null,true]}" == json_like);
}
Parse API
为了使用visitor接口,您需要调用parse API。以下是解析API:
template <typename Visitor>
bool parse(const char* data, size_t len, size_t& off, Visitor& v);
template <typename Visitor>
bool parse(const char* data, size_t len, Visitor& v);
它们类似于unpack APIs。
当您调用一个解析函数时,解析函数会回调您传递的访问者。如果解析过程成功完成,则解析函数返回true,否则返回false。如果访问者的visit函数返回false,那么parse函数将返回false。
你可以像unpacker一样使用流接口的访问者。
template <typename VisitorHolder, typename ReferencedBufferHook>
class parser
VisitorHolder需要一个名为visitor()的函数,该函数返回满足visitor概念的visitor对象的引用。
ReferencedBufferHook需要一个操作符()(char*)。仅当满足以下所有条件时,才会使用当前缓冲区指针调用运算符()
- Called parser::reserve_buffer(std::size_t).
- 当前缓冲区没有足够的大小,然后分配了新的缓冲区。
3.访客引用当前缓冲区。在这种情况下,visitor::referenced()应该返回true。
如果visitor::referenced()返回true,则parserdoesn不会释放当前缓冲区,而是调用referencedbufferhook`。因此,您可以有机会为当前缓冲区设置一些清理功能。
请参阅[unpacker的内存管理机制](unpacker’s memory management mechanism.)。
除了bool next()之外,parse与unpacker具有相同的公共成员函数。unpacker具有以下名为next的成员函数:
bool next(msgpack::object_handle& result, bool& referenced);
bool next(msgpack::object_handle& result);
parser有以下名为next的成员函数
bool next();
调用next()时,解析一个MessagePack格式的数据。所以访问者的访问函数被调用。如果解析过程成功完成,则返回true,否则返回false。
Q.E.D.