英文:
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})...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论