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

如何在不使用外部库的情况下序列化/扁平化指定C++ node结构体为字节数组或其他

Alright, let's break down how to serialize and deserialize your node struct without any external libraries. Since we're rolling our own solution, we need to handle every data type manually—strings, booleans, vectors, and those recursive node pointers—by writing/reading bytes directly to/from a buffer. Here's a practical, step-by-step approach with code examples:

Core Ground Rules

Before we jump into code, let's outline how we'll handle each data type:

  • Strings: First write the length of the string (as a fixed-size integer like uint32_t to avoid overflow), then write the string's raw bytes.
  • Booleans: Write a single byte (0 for false, 1 for true).
  • Vectors: First write the number of elements, then serialize each element one by one.
  • Recursive Nodes: Since we have vector<node*>, we'll handle null pointers explicitly (write a boolean flag first to mark if the pointer is null), then recursively serialize each non-null node.
Serialization (Flatten to Byte Array)

We'll use a std::vector<uint8_t> as our buffer—it's dynamic, easy to work with, and gives us a raw byte array we can save or transmit.

Here's the code with helper functions and recursive serialization:

#include <vector>
#include <string>
#include <cstdint>

struct node {
    std::string splitOn;
    std::string label;
    bool isLeaf;
    std::vector<std::string> childrenValues;
    std::vector<node*> children;
};

// Helper to write a primitive type (like uint32_t, bool) to the buffer
template<typename T>
void writeToBuffer(std::vector<uint8_t>& buffer, const T& value) {
    const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&value);
    buffer.insert(buffer.end(), bytes, bytes + sizeof(T));
}

// Helper to serialize a std::string
void writeString(std::vector<uint8_t>& buffer, const std::string& str) {
    uint32_t len = static_cast<uint32_t>(str.size());
    writeToBuffer(buffer, len);
    buffer.insert(buffer.end(), str.begin(), str.end());
}

// Recursive function to serialize a single node (handles null pointers)
void serializeNode(const node* n, std::vector<uint8_t>& buffer) {
    if (!n) {
        bool isNull = true;
        writeToBuffer(buffer, isNull);
        return;
    }
    bool isNull = false;
    writeToBuffer(buffer, isNull);

    // Serialize each field in order
    writeString(buffer, n->splitOn);
    writeString(buffer, n->label);
    writeToBuffer(buffer, n->isLeaf);

    // Serialize childrenValues vector
    uint32_t cvCount = static_cast<uint32_t>(n->childrenValues.size());
    writeToBuffer(buffer, cvCount);
    for (const auto& val : n->childrenValues) {
        writeString(buffer, val);
    }

    // Serialize children nodes (recursive call)
    uint32_t childCount = static_cast<uint32_t>(n->children.size());
    writeToBuffer(buffer, childCount);
    for (const auto& child : n->children) {
        serializeNode(child, buffer);
    }
}

// Top-level function to start serialization from the root node
std::vector<uint8_t> serialize(const node& root) {
    std::vector<uint8_t> buffer;
    serializeNode(&root, buffer);
    return buffer;
}
Deserialization (Reconstruct from Byte Array)

To get our node tree back from the byte array, we'll reverse the serialization process. We'll use an index tracker to keep track of where we are in the buffer, and recursively rebuild each node.

Here's the corresponding deserialization code:

// Helper to read a primitive type from the buffer
template<typename T>
T readFromBuffer(const std::vector<uint8_t>& buffer, size_t& idx) {
    T value = *reinterpret_cast<const T*>(&buffer[idx]);
    idx += sizeof(T);
    return value;
}

// Helper to deserialize a std::string
std::string readString(const std::vector<uint8_t>& buffer, size_t& idx) {
    uint32_t len = readFromBuffer<uint32_t>(buffer, idx);
    std::string str(buffer.begin() + idx, buffer.begin() + idx + len);
    idx += len;
    return str;
}

// Recursive function to deserialize a single node (handles null pointers)
node* deserializeNode(const std::vector<uint8_t>& buffer, size_t& idx) {
    bool isNull = readFromBuffer<bool>(buffer, idx);
    if (isNull) {
        return nullptr;
    }

    node* n = new node();

    // Deserialize each field in the same order as serialization
    n->splitOn = readString(buffer, idx);
    n->label = readString(buffer, idx);
    n->isLeaf = readFromBuffer<bool>(buffer, idx);

    // Deserialize childrenValues vector
    uint32_t cvCount = readFromBuffer<uint32_t>(buffer, idx);
    for (uint32_t i = 0; i < cvCount; ++i) {
        n->childrenValues.push_back(readString(buffer, idx));
    }

    // Deserialize children nodes (recursive call)
    uint32_t childCount = readFromBuffer<uint32_t>(buffer, idx);
    for (uint32_t i = 0; i < childCount; ++i) {
        n->children.push_back(deserializeNode(buffer, idx));
    }

    return n;
}

// Top-level function to start deserialization from the byte buffer
node* deserialize(const std::vector<uint8_t>& buffer) {
    size_t idx = 0;
    return deserializeNode(buffer, idx);
}
Critical Notes to Avoid Headaches
  • Endianness: The code uses your system's native endianness. If you need cross-platform compatibility (e.g., serialize on x86 and deserialize on ARM), add code to convert between host byte order and network byte order (use htonl/ntohl for uint32_t values).
  • Memory Leaks: The deserialization code uses new to create nodes. Add this helper function to recursively delete all nodes when you're done:
    void deleteNodeTree(node* root) {
        if (!root) return;
        for (auto child : root->children) {
            deleteNodeTree(child);
        }
        delete root;
    }
    
  • Cycle Detection: If your node graph has cycles (e.g., a child node pointing back to its parent), the current serialization code will loop infinitely. To fix this, track serialized nodes with an ID map—assign each node a unique ID, and write the ID instead of re-serializing the node if it's already been processed.
  • String Encoding: The code assumes std::string stores raw bytes, which works for most use cases. If you need explicit handling of multi-byte encodings (like UTF-8), you don't need to change anything—UTF-8 is just bytes, so it'll serialize/deserialize correctly.

内容的提问来源于stack exchange,提问作者Kumar Roshan Mehta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:03:55