Search code examples
c++jsondata-structuresrapidjson

Switch case for Rapidjson::Type


JSON that I'm trying to parse looks something like this is:

{
   "testBool": true,
   "testString": "eu"
}

And my current parser looks really ugly and it really feels like there is a more elegant way to solve this problem. I tried looking into rapidjson::Type for a switch case using document.GetObject().GetType() but it doesn't provide the same type precision that you can achieve by using Get%TypeName%() functions. hashmap is nothing but a wrapper around std::unordered_map<std::string, std::any>.

rapidjson::Document document;
document.Parse(tmp_string.c_str());

for (auto& member : document.GetObject())
{

if (member.value.IsBool())
{
   hashmap->addEntry<bool>(member.name.GetString(), member.value.GetBool());
}
else if (member.value.IsString())
{
   hashmap->addEntry<std::string>(member.name.GetString(), member.value.GetString());
}
else if (member.value.IsInt())
{
   hashmap->addEntry<int>(member.name.GetString(), member.value.GetInt());
}

.....
//And so on
.....

}

Solution

  • my current parser looks really ugly

    Beauty is in the eye of the be(er)holder...here's my code:

    static void
    printField(const Value& e, const string& fld, bool print_newline = true) {
        const Value &v = fld.empty() ? e : e[fld];
        if (print_newline)
            cout << endl << "\t";
        if (not fld.empty())
            cout << fld << ":  ";
        if ( /* custom stuff required? */ ) {
            // Do custom stuff
        else {
           switch (v.GetType()) {
            case kNullType:
                cout << "Null";
                break;
            case kFalseType:
            case kTrueType:
                cout << v.GetBool();
                break;
            case kObjectType: {
                bool first = true;
                cout << "{ ";
                for (const auto &subfield: v.GetObject()) {
                    if (first)
                        first = false;
                    else
                        cout << ", ";
                    printField(v, subfield.name.GetString(), false);
                }
                cout << " }";
                break;
            }
            case kArrayType: {
                bool first = true;
                cout << "[ ";
                for (const auto &arrEntry: v.GetArray()) {
                    if (first)
                        first = false;
                    else
                        cout << ", ";
                    printField(arrEntry, "", false);
                }
                cout << " ]";
                break;
            }
            case kStringType:
                cout << v.GetString();
                break;
            case kNumberType:
                if (v.IsInt64())
                    cout << v.GetInt64();
                else if (v.IsUint64())
                    cout << v.GetUint64();
                else
                    cout << v.GetDouble();
                break;
            default:
                stringstream msg;
                msg << "Unexpected RapidJSON Value type: " << v.GetType();
                throw logic_error(msg.str());
            }
        }
    }
    

    This uses the stringize stuff to solve some problems, but, if you don't like that, you can get the same effect manually. It subdivides the IsNumber case using a cascading if; if you need more resolution, you can add the other cases to that.