Tencent / rapidjson

A fast JSON parser/generator for C++ with both SAX/DOM style API
http://rapidjson.org/
Other
14.26k stars 3.53k forks source link

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

Open superwills opened 3 years ago

superwills commented 3 years ago

I've cross posted this with stackoverflow

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);
        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);
            return 0;
        }

        if (!v.HasMember(p))
        {
            printf("  - Error while digging: %s not found, property %s\n", p, propertyName);
            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;
}
miloyip commented 3 years ago
v = v[p];  // advance deeper into the tree

In C++, when a reference is created, it is bound to a value. The binding can not be modified afterwards. The assignment above will write a new value to the original bound value. This is not quite related to RapidJSON per se.