Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

json::contains usage to find a path #1727

Closed
devhandle opened this issue Aug 27, 2019 · 9 comments · Fixed by #1741
Closed

json::contains usage to find a path #1727

devhandle opened this issue Aug 27, 2019 · 9 comments · Fixed by #1741
Assignees
Labels
confirmed kind: bug release item: 🐛 bug fix solution: proposed fix a fix for the issue has been proposed and waits for confirmation
Milestone

Comments

@devhandle
Copy link

devhandle commented Aug 27, 2019

  • 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

@pboettch
Copy link
Contributor

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?

@devhandle
Copy link
Author

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

@devhandle
Copy link
Author

devhandle commented Aug 27, 2019

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.

@nlohmann
Copy link
Owner

nlohmann commented Aug 27, 2019

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

@nlohmann nlohmann added the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Aug 27, 2019
@devhandle
Copy link
Author

devhandle commented Aug 27, 2019

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.

@nlohmann nlohmann added confirmed kind: bug and removed kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation labels Sep 2, 2019
@nlohmann
Copy link
Owner

nlohmann commented Sep 2, 2019

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?

@nlohmann nlohmann added the state: help needed the issue needs help to proceed label Sep 2, 2019
@devhandle
Copy link
Author

devhandle commented Sep 4, 2019

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();
}

@tete17
Copy link
Contributor

tete17 commented Sep 7, 2019

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 :)

@devhandle
Copy link
Author

Thanks @tete17!

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

@nlohmann nlohmann added release item: 🐛 bug fix solution: proposed fix a fix for the issue has been proposed and waits for confirmation and removed state: help needed the issue needs help to proceed labels Sep 16, 2019
@nlohmann nlohmann self-assigned this Sep 16, 2019
@nlohmann nlohmann added this to the Release 3.7.1 milestone Sep 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed kind: bug release item: 🐛 bug fix solution: proposed fix a fix for the issue has been proposed and waits for confirmation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants