Poco C++ building nested JSON objects

stebl picture stebl · Mar 7, 2015 · Viewed 9.3k times · Source

I have a nested JSON object. I'm trying to build it in a function and add the inner object to the original, but I can't extract the result.

void build_object (Poco::JSON::Object * const result)
{

    /* Construct some int/bool/string fields here */

    Poco::JSON::Object inner;
    inner.set("some_number", 5);
    inner.set("some_string", "xyz");

    /* This is where it breaks down */ 
    std::string key = "new_object";
    result->set("new_object", inner);

    /* Then some debugging and testing */
    // The new object is printed inside the first -> seems like it's working
    result->stringify(std::cout); 

    printf("result has(key): %i\n", result->has(key)); // true
    printf("isObject: %i\n", result->isObject(key));   // false - huh?
    printf("isNull: %i\n", result->isNull(key));       // false
    printf("isArray: %i\n", result->isArray(key));     // false

    Poco::JSON::Object::Ptr ptr = result->getObject(key); 
    // unsurpisingly fails since the above indicates it's not an object
    printf("ptr isNull: %i\n", ptr.isNull());          // true
    // ptr->has("some_number"); // throws NullPointerException

    // if it's not an object/null/array, it must be a value
    Poco::Dynamic::Var v = result->get(key);
    // at least one of these things should be true, otherwise what is it?
    printf("var isString: %i\n", v.isString());  // false
    printf("var isStuct: %i\n", v.isStruct());   // false
    printf("var isEmpty: %i\n", v.isEmpty());    // false
    printf("var isArray: %i\n", v.isArray());    // false
    printf("var isSigned: %i\n", v.isSigned());  // false
    printf("var isNumeric: %i\n", v.isNumeric());// false
}

So, I have an inner object that is correctly put into the result, it is being printed via stringify with all the correct values and result->has() is successful. But, according to the result, it is not an object, array, or null, so you should be able to get it with var. But, once it's gotten from var, it's not a string, struct, array, or number, and it's also not empty. The inner object seems to exist and not exist at the same time.

So, how do I put this object into my result? And how do I get it out?

Thanks

note: I've seen this thread Correct usage of Poco C++ JSON for parsing data, but it's building the JSON object from string and then parsing it. I suppose I could build everything as a string and convert to the Poco Object at the last step, but I'm still curious why the above is happening. Also, using result->set() and result->get() are a cleaner, less hack-y solution than going through a string.

References: Poco JSON Doc, Poco Dynamic Var Doc

Answer

Alex picture Alex · Mar 8, 2015

Poco::JSON Objects and Arrays are held as shared pointers internally by default (optimization to avoid values copying) and everything is Dynamic::Var, so it works for both pointers and values. When you insert an Object as value it works because Dynamic::Var will hold pretty much anything, but the problem you experience when inspecting it comes from the fact that internal comparison does not return true for Object values because it compares only with default type - Poco::SharedPtr<Poco::JSON::Object>.

Here's a workaround:

void build_object (Poco::JSON::Object * const result)
{
    // smart pointer, so don't worry about cleaning up
    Poco::JSON::Object::Ptr inner = new Poco::JSON::Object;
    inner->set("some_number", 5);
    inner->set("some_string", "xyz");

    std::string key = "new_object";
    result->set(key, inner);
    printf("isObject: %i\n", result->isObject(key)); // true
}

I have opened a github issue to alleviate this caveat.