ibireme / yyjson

The fastest JSON library in C
https://ibireme.github.io/yyjson/doc/doxygen/html/
MIT License
2.98k stars 262 forks source link

memory issue for recursive function #151

Closed Edwardmark closed 6 months ago

Edwardmark commented 6 months ago

I have a TreeNode class which defines a tree structure. And I want to convert it to json using yyjson.

yyjson_mut_val *convertTreeNodeToJSON(const std::shared_ptr<TreeNode> &node, yyjson_mut_doc *doc)
{
    yyjson_mut_val *jsonNode = nullptr;
    yyjson_mut_val *subNode = nullptr;

    if (node->GetValue().empty())
    {
        // Object or Array node
        bool isArrayNode = false;

        // Check if all siblings has same name, it true, then it is array
        auto parent = node->GetParent();
        if (parent)
        {
            auto sameKeyChildren = parent->GetChild(node->GetName());
            if (sameKeyChildren.size() == parent->GetChildren().size())
            {
                isArrayNode = true;
            }
        }

        if (isArrayNode)
        {
            jsonNode = yyjson_mut_arr(doc);
            for (const auto &child : node->GetChildren())
            {
                subNode = convertTreeNodeToJSON(child, doc);
                yyjson_mut_arr_add_val(jsonNode, subNode);
            }
        }
        else
        {
            jsonNode = yyjson_mut_obj(doc);

            for (const auto &child : node->GetChildren())
            {
                subNode = convertTreeNodeToJSON(child, doc);
                yyjson_mut_val *key = yyjson_mut_str(doc, child->GetName().c_str());
                yyjson_mut_obj_add(jsonNode, key, subNode);
            }
        }
    }
    else
    {
        // Value node
        jsonNode = yyjson_mut_str(doc, node->GetValue().c_str());
    }
    // when downstream recursion end, the json_str is null, invalid
    size_t json_len;
    char *json_str = yyjson_mut_val_write(jsonNode, YYJSON_WRITE_PRETTY, &json_len);
    std::cout << json_str << std::endl;
    return jsonNode;
}

int main()
{
    // Example tree
    std::shared_ptr<TreeNode> root = std::make_shared<TreeNode>();
    root->SetName("root");

    std::shared_ptr<TreeNode> objNode = std::make_shared<TreeNode>();
    objNode->SetName("object");

    std::shared_ptr<TreeNode> key1 = std::make_shared<TreeNode>("key1", "value1");
    std::shared_ptr<TreeNode> key2 = std::make_shared<TreeNode>("key2", "value2");

    objNode->AddChild(key1);
    objNode->AddChild(key2);

    std::shared_ptr<TreeNode> arrayNode = std::make_shared<TreeNode>("array");
    std::shared_ptr<TreeNode> item1 = std::make_shared<TreeNode>("key", "item1");
    std::shared_ptr<TreeNode> item2 = std::make_shared<TreeNode>("key", "item2");

    arrayNode->AddChild(item1);
    arrayNode->AddChild(item2);

    root->AddChild(objNode);
    root->AddChild(arrayNode);

    // Convert tree to JSON
    yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
    yyjson_mut_val *json = convertTreeNodeToJSON(root, doc);
    yyjson_mut_doc_set_root(doc, json);

    // Convert JSON to string
    size_t json_len;
    char *json_str = yyjson_mut_val_write(json, YYJSON_WRITE_PRETTY, &json_len);
    std::cout << json_str << std::endl;
    // Cleanup
    yyjson_mut_doc_free(doc);
}

The expected output should be:

{
  "root": {
    "object": {
      "key1": "value1",
      "key2": "value2"
    },
    "array": [
      "item1",
      "item2"
    ]
  }
}

But the actual result in main is NULL, and the cout << json_str << endl; in the recursion function outputs:

"item1"
"item2"
{
    "key": "item2",
    "key": "item2"
}
"value1"
"value2"
{
    "key2": "value2",
    "key2": "value2"
}
{
    "objec": {
        "key": "\u0000\u0000\u0000\u0000\u0000",
        "key": "\u0000\u0000\u0000\u0000\u0000"
    },
    "object": {
        "key2": "\u0000\u0000\u0000\u0000\u0000\u0000",
        "key2": "\u0000\u0000\u0000\u0000\u0000\u0000"
    }
}

It seems that the result is destructed when the function call end, so it returns null. So what is the problem? Why the result is null? Could you please give me some hits? Thanks. @TkTech @lundmark @ibireme @nixzhu @fd00 Thanks.

nixzhu commented 6 months ago

@Edwardmark I don't have much experience with cpp, but you are passing the same doc pointer to the recursive function convertTreeNodeToJSON, is this a problem?

Edwardmark commented 6 months ago

@nixzhu Thanks for you reply. I want to convert a tree structure to json, so I have to use the same doc pointer. The problem is about the jsonNode or SubNode in the recursion function. I print the value, it shows when the bottom recursion end, the upper function call cannot get their value, print result is null.

Edwardmark commented 6 months ago

@Edwardmark I don't have much experience with cpp, but you are passing the same doc pointer to the recursive function convertTreeNodeToJSON, is this a problem?

It is not about cpp, I want to verify if the doc owns its subnode memory when calling the recursion function? And what should I do to fix the issue? Thanks .

ibireme commented 6 months ago

@Edwardmark The doc holds memory for all json values, so the recursive call seems good.

There are two string handling issues:

  1. yyjson_mut_str() doesn't take ownership of the string. If the passed string is modified or freed during use, errors like \u0000 may occur. Consider using yyjson_mut_strcpy() instead.
  2. The string returned by yyjson_mut_val_write() needs to be freed after use.

If you share the TreeNode source code, we can better understand and replicate the issue.

Edwardmark commented 6 months ago

@ibireme Thanks for your reply. It is the problem. I change yyjson_mut_str() to yyjson_mut_strcpy() and it worked. And for problem2, how to free the string? Could you give me a example? Thanks.

ibireme commented 6 months ago

@ibireme Thanks for your reply. It is the problem. I change yyjson_mut_str() to yyjson_mut_strcpy() and it worked. And for problem2, how to free the string? Could you give me a example? Thanks.

Just call free(json_str).

Edwardmark commented 6 months ago

@ibireme Thanks.