You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于redis-cpp库实现Redis中哈希类型的RESP序列化与反序列化

Adding RESP Hash Support to redis-cpp

Got it, let's break down how to add hash type serialization/deserialization to your redis-cpp setup. First, a quick reminder: RESP doesn't have a dedicated hash marker. Redis represents hashes as arrays of alternating key-value pairs (so an array with an even number of elements). We'll model this in our code, matching the pattern of the existing simple_string and other types.


Step 1: Implement Serialization for Hash

Add this class to your serialization.h file, alongside the existing simple_string and other serialization types:

class hash final {
public:
    // Use a vector of key-value pairs; value can be any serializable RESP type
    using KVPair = std::pair<std::string_view, std::variant<simple_string, bulk_string, integer>>;
    using value_type = std::vector<KVPair>;

    explicit hash(value_type kv_pairs) noexcept : kv_pairs_(std::move(kv_pairs)) {}

    void put(std::ostream &stream) {
        // Hash is serialized as an array with length = 2 * number of key-value pairs
        stream << detail::marker::array 
               << kv_pairs_.size() * 2 
               << detail::marker::cr << detail::marker::lf;

        // Serialize each key-value pair (keys are always bulk strings per Redis conventions)
        for (const auto &[key, value] : kv_pairs_) {
            put(stream, bulk_string{key});
            // Use std::visit to call the correct put method for the value type
            std::visit([&stream](const auto &val) { put(stream, val); }, value);
        }
    }

private:
    value_type kv_pairs_;
};

Step 2: Implement Deserialization for Hash

Add this class to your deserialization.h file, matching the structure of the existing deserialization types:

class hash final {
public:
    explicit hash(std::istream &stream) {
        // First read the array length (hashes require even-length arrays)
        std::string len_str;
        std::getline(stream, len_str);
        len_str.pop_back(); // Remove trailing '\r'
        size_t arr_len = std::stoull(len_str);

        if (arr_len % 2 != 0) {
            throw std::invalid_argument(
                "[rediscpp::resp::deserialization::hash] Hash array must have even length."
            );
        }

        kv_pairs_.reserve(arr_len / 2);
        for (size_t i = 0; i < arr_len; i += 2) {
            // Read the key (always a bulk string in Redis hashes)
            auto key = get<bulk_string>(stream);
            
            // Detect the value type and deserialize it
            char mark = get_mark(stream);
            switch (mark) {
                case detail::marker::simple_string:
                    kv_pairs_.emplace_back(key.get(), get<simple_string>(stream));
                    break;
                case detail::marker::bulk_string:
                    kv_pairs_.emplace_back(key.get(), get<bulk_string>(stream));
                    break;
                case detail::marker::integer:
                    kv_pairs_.emplace_back(key.get(), get<integer>(stream));
                    break;
                default:
                    throw std::invalid_argument(
                        "[rediscpp::resp::deserialization::hash] Unsupported value type in hash."
                    );
            }
        }
    }

    [[nodiscard]] const auto& get() const noexcept {
        return kv_pairs_;
    }

private:
    std::vector<std::pair<std::string_view, std::variant<simple_string, bulk_string, integer>>> kv_pairs_;
};

Step 3: Update Your Test Data

Modify your make_sample_data() function to include a hash:

auto make_sample_data() {
    std::ostringstream stream;
    put(stream, resps::array{
        resps::simple_string{"This is a simple string."},
        resps::error_message{"This is an error message."},
        resps::bulk_string{"This is a bulk string."},
        resps::integer{100500},
        resps::array{
            resps::simple_string("This is a simple string in a nested array."),
            resps::bulk_string("This is a bulk string in a nested array.")
        },
        // Add your test hash here
        resps::hash({
            {"username", resps::bulk_string{"john_doe"}},
            {"age", resps::integer{35}},
            {"account_status", resps::simple_string{"active"}}
        })
    });
    return stream.str();
}

Step 4: Update Your Printing Logic

You'll need to extend your print_sample_data() function to handle hash values. Here's an example of how to add that support (add this to your existing visitor logic):

// Inside your print_sample_data's variant visitor
if constexpr (std::is_same_v<std::decay_t<decltype(val)>, respds::hash>) {
    out << "Hash (" << val.get().size() << " entries):\n";
    for (const auto &[key, value] : val.get()) {
        out << "  " << key << ": ";
        // Recursively print the value
        std::visit([&out](const auto &v) { out << v.get(); }, value);
        out << "\n";
    }
}

Key Notes

  • RESP Hash Representation: Hashes are always arrays under the hood—this is how Redis encodes both hash commands (like HSET) and hash responses.
  • Type Safety: We use std::variant to support multiple value types (simple strings, bulk strings, integers) in the hash. You can extend this to support more types if needed.
  • Validation: The deserialization code checks for even-length arrays, which is required for valid Redis hashes.

内容的提问来源于stack exchange,提问作者Georgia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 01:12:33