Skip to content

Using std::variant with std::unordered_map can throw uncatchable exception #631

@pfrenkel

Description

@pfrenkel

Describe the bug

When using a std::variant with std::unordered_map types mixed with std::string (amongst others) as its types, deserialisation via
jsoncons::decode_json<...>(...); can throw an uncatchable exception, and the application will terminate with terminate called after throwing an instance of 'jsoncons::json_runtime_error<std::domain_error, void>' what(): Not an object. The can is because the variant types order matters. If i push the std::unordered_map to the back of the variant, it will correctly parse data.

The "Not an object" seems to come from jsoncons trying to interpret a JSON string as object ("" vs {}) - although i am not really sure. (I know of the case where it is not possible to distinguish an string enum and a string value, depending on order, see #257).

Expected behavior:
-correctly detect and distinguish "" versus {} inside a variant type using std::string and std::unordered_map when decoding JSON strings
-throw a catchable exception

Current behavior:
Throws an uncatchable exception and thus terminates.

Please consider following example code, where parsing sJson1 will throw and will not be caught by the exception handler (i also tried catching (...), to no avail). When parsing sJson2 it works as expected.

#include <jsoncons/json.hpp>

#include <stdio.h>
#include <unordered_map>
#include <vector>
#include <string>

//////////////////////////////////////////////////////////////////////////////

typedef std::variant<
    std::unordered_map<std::string, std::string>,
    std::unordered_map<std::string, std::unordered_map<std::string, std::string>>,
    std::string,
    std::vector<std::string>,
    int64_t,
    std::vector<int64_t>,
    double,
    std::vector<double>,
    bool,
    std::vector<bool>
>  
VARIANTTYPE;

//////////////////////////////////////////////////////////////////////////////

class SerialisableClass
{
public:
    SerialisableClass()=default;

    std::string m_sStr;
    VARIANTTYPE m_data;
};

JSONCONS_ALL_MEMBER_NAME_TRAITS(SerialisableClass,
    (m_sStr, "str"),
    (m_data, "data")
)
//////////////////////////////////////////////////////////////////////////////

int main(int argc, char** argv)
{
    std::string sJson1 = R"(
    {
        "str": "string_value1",
        "data": "string_value2"
    }
    )";
    std::string sJson2 = R"(
    {
        "str": "string_value",
        "data": {
            "key1": "value1"
        }        
    }
    )";
    try
    {
        SerialisableClass sclass = jsoncons::decode_json<SerialisableClass>(sJson1);
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }

    return 0;
}

//////////////////////////////////////////////////////////////////////////////

Compiler/OS:

  • Compiler: g++ 13.3.0-6ubuntu2~24.04

  • Architecture (e.g. x86, x64) x64, WSL2

  • Operating system: ubuntu 24.04 inside WSL2

  • Build command used: g++ -g -fexceptions -Wall -Wextra -pedantic -fPIC -I ../jsoncons/include/ main2.cpp -o main2

  • Compiler: cl.exe 19.29.30159 for x64

  • Architecture (e.g. x86, x64) x64

  • Operating system: Windows 10

  • Build command used: cl.exe /W4 /std:c++17 /Zi /EHa /nologo /MD /I../jsoncons/include/ /FeC:\code\enef\main2.exe C:\code\enef\main2.cpp

What jsoncons library version?

  • Latest release 1.4.0
  • Other release ______
  • master

Side note: Thank you so much for an awesome OSS library!

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions