json::contains usage to find a path

Created on 27 Aug 2019  路  9Comments  路  Source: nlohmann/json

  • Describe what you want to achieve.

I want to test whether a path exists in a json file. For example, "_/root/settings/logging_".

  • Describe what you've tried.
auto jPtr = json::json_pointer("/root/settings/logging"_json_pointer);
auto exists1 = jsonfile.contains(jPtr);

auto exists2 = jsonfile.contains(json::json_pointer("/root/settings/logging"_json_pointer));

exists1 is false and exists2 is true.

I expected both of these to be true. Is this a bug or is this expected behaviour?

UPDATE: There are two contains functions. One which takes an rvalue reference to a KeyT and one which takes a reference to a json_pointer. Both do different things. This seems inconsistent to me. If I std::move jPtr it works but I don't want to move the pointer.

  • Describe which system (OS, compiler) you are using.

g++ (Ubuntu 8.3.0-6ubuntu1~18.10.1) 8.3.0

  • Describe which version of the library you are using (release version, develop branch).

v3.7.0

confirmed bug release item bug fix proposed fix

All 9 comments

auto jPtr = json::json_pointer("/root/settings/logging"_json_pointer);

There is maybe one _json_pointer too much here?

Isn't it either

auto jPtr = json::json_pointer("/root/settings/logging");

or

auto jPtr = "/root/settings/logging"_json_pointer;

What do you see when printing your jPtr?

Thanks for replying but I've tested your suggestions and neither of them work.

at seems to behave correctly. I haven't tested it fully yet but the code below works (outputsExists). I'd want to use contains as it doesn't throw an exception.

    try
    {
        json::json_pointer p {"/root/settings/logging"};
        jsonfile.at(p);
        std::cout << "Exists" << std::endl;
    }
    catch (const json::exception &e)
    {
        std::cout << "Doesn't exist" << std::endl;
    }

Also, is there any way to enumerate all key value pairs in a json file? The items() doesn't return all keys/values.

Indeed there is a double invocation of the JSON Pointer constructor if you call

json::json_pointer("/root/settings/logging"_json_pointer)

For your example, this code works:

#include "json.hpp"
#include <iostream>

using json = nlohmann::json;

int main()
{
    json j;
    j["root"]["settings"]["logging"] = true;

    std::cout << std::boolalpha
        << j.contains("/root/settings/logging"_json_pointer) << '\n'
        << j.contains("/no/value/here"_json_pointer)
        << std::endl;
}

Output:

true
false

Thanks but my question was about passing an instance of json::json_pointer and not hard coded paths.

It is related to the contains function and it's different implementations for const and non-const json_pointers.

auto jptr1= "/root/settings/logging"_json_pointer;
auto jptr2= json::json_pointer{"/root/settings/logging"};

std::cout << std::boolalpha << j.contains(jptr1);
std::cout << std::boolalpha << j.contains(jptr2);

....both return false. But if I make them both const, they return true!

const auto jptr1= "/root/settings/logging"_json_pointer;
const auto jptr2= json::json_pointer{"/root/settings/logging"};

std::cout << std::boolalpha << j.contains(jptr1);
std::cout << std::boolalpha << j.contains(jptr2);

Is this a bug because this really caught me out.

I see.

Calling contains with a non-const json_pointer calls

template<typename KeyT, typename std::enable_if<
             not std::is_same<KeyT, json_pointer>::value, int>::type = 0>
bool contains(KeyT && key) const
{
    return is_object() and m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();
}

which is wrong, whereas calling it with a const json_pointer calls

bool contains(const json_pointer& ptr) const
{
    return ptr.contains(this);
}

which is correct.

I am puzzled. Any ideas?

I'm not familiar enough with the codebase to suggest a proper fix but I've added a contains function which accepts a non const json_pointer and this works.

bool contains(json_pointer& ptr) const
{
    return ptr.contains(this);
}

Could you explain to me what the std::enable_if is mean't to achieve? Is it to prevent calls passing a json_pointer parameter because it does not do this. Is this a bug?

template<typename KeyT, typename std::enable_if<
             not std::is_same<KeyT, json_pointer>::value, int>::type = 0>
bool contains(KeyT && key) const
{
    return is_object() and m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();
}

Hey @devhandle I have explained why this wasn't working in the pull requests but if you need more explanation from my side, don't doubt into dropping me a message :)

Thanks @tete17!

I've tested your change and it fixes my issue. Also, thanks for the explanation - much appreciated.

Was this page helpful?
0 / 5 - 0 ratings