Search code examples
c++rapidjson

Inconsistent IsObject() property in searching through nested properties in rapidjson document


I'm having an issue where the rapidjson library appears to be inconsistent as to when it reports IsObject() as true.

Sometimes when I call value.IsObject() after retrieving a Value from a Document, it correctly reports it as an Object. But sometimes it reports what I think should be an Object as not an Object.

A toy program is below.

It seems I am unintentionally mutating the document somehow, as the 2nd lookup for vegetables::celery remarkably fails.

#define _CRT_SECURE_NO_DEPRECATE

#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <string.h>
#include <stdio.h>
using namespace rapidjson;

int getInt(Document& jsonDoc, const char* propertyName)
{
    if (!jsonDoc.IsObject()) {
        puts("Err: jsonDoc not an object");
        return 0;
    }

    char* str = strdup(propertyName);
    char* p = strtok(str, ":");
    printf("Looking for property `%s`\n", p);
    if (!jsonDoc.HasMember(p))
    {
        printf("  - Error: %s not found, property %s\n", p, propertyName);
        free(str);
        return 0;
    }
    else
    {
        printf("  - found property '%s'\n", p);
    }

    rapidjson::Value& v = jsonDoc[p];
    
    while (p = strtok(0, ":"))
    {
        printf("Looking for property `%s`\n", p);
        if (v.IsObject())
        {
            puts("  - v is an object so I can look");
        }
        else
        {
            printf("  - ERROR: v is NOT an object, I can't search for %s, property %s not found\n", p, propertyName);
            free(str);
            return 0;
        }

        if (!v.HasMember(p))
        {
            printf("  - Error while digging: %s not found, property %s\n", p, propertyName);
            free(str);
            return 0;
        }
        else
            printf("  - found property '%s'\n", p);

        // otherwise,
        v = v[p];  // advance deeper into the tree
    }

    int val = v.GetInt();
    printf("  - json got value %s=%d\n", propertyName, val);
    free(str);
    return val;
}

void test1()
{
    const char* json = R"STR({
      "fruits":{
        "apples":1,
        "oranges":553,
        "bananas":900
      },
      "vegetables":{
        "celery":10000,
        "cabbage":10000
      }
    })STR";

    Document d;
    d.Parse(json);

    int apples = getInt(d, "fruits::apples");
    int oranges = getInt(d, "fruits::oranges");
    int bananas = getInt(d, "fruits::bananas");

    int celery = getInt(d, "vegetables::celery");
    celery = getInt(d, "vegetables::celery");
    int cabbage = getInt(d, "vegetables::cabbage");
}

int main()
{
    test1();
    return 0;
}

Solution

  • Two issues here.

    First and main:

     rapidjson::Value& v = jsonDoc[p];
     ...
     v = v[p];  
    

    Since v is not a variable, but a reference, you don't set it to a new object, but change the original object it references to, damaging it.

    Use rapidjson library Pointers if you need to change the object beneath or you can use a trick like here:

     rapidjson::Value* v = &(jsonDoc[p]);
     ...
     v = &(*v)[p]; 
    

    Second

    In some returns in if-else you have the memory leak with str, you free it only at the end of the function.