如何解决cereal C++中的JSON序列化错误?

huangapple go评论68阅读模式
英文:

How to resolve JSON serialization error with cereal c++?

问题

I encountered the following runtime error with cereal when trying to serialize the class with JSON:

libc++abi: terminating with an uncaught exception of type cereal::RapidJSONException: rapidjson internal assertion failure: IsObject()

Interestingly, absolutely the same code works with a binary archive.

I'm trying to make a template function for my code. Something like that:

enum SER_Archive_Type {BIN, JSON};

template<typename T>
bool SerializeObject(T &obj, std::string filename, const SER_Archive_Type TYPE) {
    std::fstream fs;

    switch (TYPE) {
    case JSON: {
        fs.open(filename, std::ios::out);
        if (fs.is_open()) {
            cereal::JSONOutputArchive jarchive(fs);
            jarchive(obj);
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }
    case BIN: {
        fs.open(filename, std::ios::out | std::ios::binary);
        if (fs.is_open()) {
            cereal::BinaryOutputArchive barchive(fs);
            barchive(obj);
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }
    default: 
        break;
    }

    return false;
}

template<typename T>
bool DeserializeObject(T &obj, std::string filename, const SER_Archive_Type TYPE) {
    std::fstream fs;

    switch (TYPE) {
    case JSON: {
        fs.open(filename, std::ios::in);
        if (fs.is_open()) {
            cereal::JSONInputArchive jarchive(fs);
            jarchive(obj);
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }
    case BIN: {
        fs.open(filename, std::ios::in | std::ios::binary);
        if (fs.is_open()) {
            cereal::BinaryInputArchive barchive(fs);
            barchive(obj);
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }
    default: 
        break;
    }

    return false;
}

Example code looks like this:

int main()
{
    uint32_t a = 123;
    SerializeObject(a, "a.bin", BIN);   // ok

    uint32_t b;
    DeserializeObject(b, "a.bin", BIN); // ok
    cout << b << endl; 

    uint32_t c = 321;
    SerializeObject(c, "c.txt", JSON);   // ok

    uint32_t d;
    DeserializeObject(d, "c.txt", JSON); // error
    cout << b << endl;
}

I found out that the top-level node in the generated JSON file is not closed. See the content of c.txt file from the example:

{
    "value0": 321
}

This must be incorrect behavior or I'm missing something?
I don't know how to resolve this correctly.
Thank you in advance for your help!

英文:

I encountered the following runtime error with cereal when trying serialize the class with JSON:

libc++abi: terminating with uncaught exception of type cereal::RapidJSONException: rapidjson internal assertion failure: IsObject()

Interestingly, absolutely same code works with binary archive.

I'm trying to make a template function for my code. Something like that:

enum SER_Archive_Type {BIN, JSON};

template<typename T>
bool SerializeObject(T &obj, std::string filename, const SER_Archive_Type TYPE) {
    std::fstream fs;

    switch (TYPE) {
    case JSON: {
        fs.open(filename, std::ios::out);
        if (fs.is_open()) {
            cereal::JSONOutputArchive jarchive(fs);
            jarchive(obj);
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }
    case BIN: {
        fs.open(filename, std::ios::out | std::ios::binary);
        if (fs.is_open()) {
            cereal::BinaryOutputArchive barchive(fs);
            barchive(obj);
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }
    default: 
        break;
    }

    return false;
}

template<typename T>
bool DeserializeObject(T &obj, std::string filename, const SER_Archive_Type TYPE) {
    std::fstream fs;

    switch (TYPE) {
    case JSON: {
        fs.open(filename, std::ios::in);
        if (fs.is_open()) {
            cereal::JSONInputArchive jarchive(fs);
            jarchive(obj);
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }
    case BIN: {
        fs.open(filename, std::ios::in | std::ios::binary);
        if (fs.is_open()) {
            cereal::BinaryInputArchive barchive(fs);
            barchive(obj);
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }
    default: 
        break;
    }

    return false;
}

Example code looks like that:

int main()
{
    uint32_t a = 123;
    SerializeObject(a, "a.bin", BIN);   // ok

    uint32_t b;
    DeserializeObject(b, "a.bin", BIN); // ok
    cout << b << endl; 

    uint32_t c = 321;
    SerializeObject(c, "c.txt", JSON);   // ok

    uint32_t d;
    DeserializeObject(d, "c.txt", JSON); // error
    cout << b << endl;
}

I found out that the top level node in the generated JSON file is not closed. See the content of c.txt file from the example:

{
    "value0": 321

This must be incorrect behavior or I'm missing something?
I don't know how to resolve this correctly.
Thank you in advance for your help!

答案1

得分: 0

明显地,我还不太了解档案的工作原理。解决方案将是:

case JSON: {
        fs.open(filename, std::ios::out);
        if (fs.is_open()) { 
            { // to finish json inside of brackets and call destructor... 
                cereal::JSONOutputArchive jarchive(fs);
                jarchive(obj);
            }
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }
英文:

Apparently, I haven't understood how archives work. The solution will be:

case JSON: {
        fs.open(filename, std::ios::out);
        if (fs.is_open()) { 
            { // to finish json inside of brackets and call destructor... 
                cereal::JSONOutputArchive jarchive(fs);
                jarchive(obj);
            }
            fs.close();
            return true;
        } else {
            return false;
        }
        break;
    }

答案2

得分: 0

以下是翻译好的部分:

寿命问题可以更容易地解决:

if (std::ofstream file{filename}) 
    //文件属于`if`范围:无需关闭。
    cereal::JSONOutputArchive{file}(obj);
    /*存档是右值,如果没有被引用捕获,会立即销毁。*/

注意:由于我使用ofstream而不是fstream,所以ios_base::out标志是多余的。类似地,对于输入文件,您可以使用ifstream。对于二进制文件,仍然需要使用ios_base::binary

if (std::ofstream file{filename, std::ios_base::binary})...
英文:

Lifetime issue can be solved much easier:

if (std::ofstream file{filename}) 
    //file belongs to `if` scope: no need to close.
    cereal::JSONOutputArchive{file}(obj);
    /*Archive is rvalue and is destructed
    immediately if not captured by a reference.*/

Note: since I use ofstream instead of fstream, the ios_base::out flag is redundant. Similarly for input files, you can use ifstream. The ios_base::binary is still needed for binary files:

if (std::ofstream file{filename, std::ios_base::binary})...

huangapple
  • 本文由 发表于 2023年5月14日 10:43:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/76245603.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定