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

Request: range-based-for over a json-object to expose .first/.second #350

Closed
jaredgrubb opened this issue Nov 2, 2016 · 21 comments · Fixed by #578
Closed

Request: range-based-for over a json-object to expose .first/.second #350

jaredgrubb opened this issue Nov 2, 2016 · 21 comments · Fixed by #578
Assignees
Labels
kind: question solution: wontfix the issue will not be fixed (either it is impossible or deemed out of scope)
Milestone

Comments

@jaredgrubb
Copy link
Contributor

I would love to have the ability to iterate over a JSON object type in exactly the same way I would iterate over a std::map:

for(auto& i : map) { i.first; i.second; }

Would it be possible to have a free function (or member) that would allow me to do something like:
for(auto& i : as_map(jsonObj)) {...}
such that the iterator exposed here works more like map::iterator (.first,.second) than the current iterator type (.key,.value).

@nlohmann
Copy link
Owner

nlohmann commented Nov 2, 2016

I did not find a way to realize this without any syntactic adjustment, but you can do this:

json j = {{"A", 1}, {"B", 2}};

for (auto i : json::iterator_wrapper(j))
{
  auto x = i.key(); // x = "A", x = "B"
  auto y = i.value(); // y = 1, y = 2
}

See https://github.com/nlohmann/json/blob/develop/test/src/unit-iterator_wrapper.cpp for more examples.

If you have any idea to realize this without a wrapper, please let me know!

@jaredgrubb
Copy link
Contributor Author

I think it will require:

  • a wrapper function
  • a new iterator type (whose value type is pair<string,json> instead of key/value).

Personally, I really dont like the key/value functions and would rather just use first/second ... and this is the minimal approach I can think of. What do you think? I'm happy to start working on a patch, but if you think it's a non-starter, then I'll skip it.

@nlohmann
Copy link
Owner

nlohmann commented Nov 8, 2016

So basically the syntax of the loop would be the same, but you'd rather write i.first than i.key()?

@jaredgrubb
Copy link
Contributor Author

My goal is to make range-for over a json-object easy, which really doesnt work right now (well without your "iterator_wrapper" suggestion, which is not very discoverable). I personally prefer .first/.second because I want it to feel like a map, but that's bikeshedding.

Honestly, none of these sound like awesome solutions. Another option is to use get_ref to get the internal STL map object, but that's even worse to type.

@nlohmann
Copy link
Owner

So again - if there would be a better name for the iteration_wrapper (maybe also a more prominent documentation), and additional first/second members - would you see this as an improvement? Any name suggestions?

@nlohmann nlohmann added this to the Release 2.0.8 milestone Nov 15, 2016
@nlohmann nlohmann added the state: please discuss please discuss the issue or vote for your favorite option label Nov 16, 2016
@nlohmann nlohmann removed this from the Release 2.0.8 milestone Nov 23, 2016
@nlohmann
Copy link
Owner

Any opinions on this?

@nlohmann nlohmann added the state: help needed the issue needs help to proceed label Dec 2, 2016
@nlohmann
Copy link
Owner

nlohmann commented Dec 2, 2016

I once again shall try to get another opinion on how to name the iteration_wrapper...

@jaredgrubb
Copy link
Contributor Author

make_iterator(json&)?

@jaredgrubb
Copy link
Contributor Author

make_object_iterator(json&)?

@nlohmann nlohmann added this to the Release 3.0.0 milestone Dec 12, 2016
@nlohmann
Copy link
Owner

I'm still not too happy about the namings. In any case, does adding first/second as aliases for key() and value() make sense?

@holocronweaver
Copy link

I would rather have a member function than a free function for sake of reading and typing. Something like for (auto& i : j.range()) over for (auto& i : json::range(j)).
key() and value() are good enough for me, but aliases will probably ease in new users.
❤️ this library BTW.

@Rikorose
Copy link

The aliases could be added easily. Member variables with key and value aka first and second are more difficult though.

/*!
@copydoc iterator_wrapper(reference)
*/
static iteration_proxy<iterator> range(reference cont)
{
    return iteration_proxy<iterator>(cont);
}

/*!
@copydoc iterator_wrapper(reference)
*/
static iteration_proxy<const_iterator> range(const_reference cont)
{
    return iteration_proxy<const_iterator>(cont);
}

/*!
@copydoc iterator_wrapper(reference)
*/
iteration_proxy<iterator> range()
{
    return iteration_proxy<iterator>(*this);
}

/*!
@copydoc iterator_wrapper(reference)
*/
iteration_proxy<const_iterator> range() const
{
    return iteration_proxy<const_iterator>(*this);
}

nlohmann added a commit that referenced this issue May 9, 2017
Not working yet.
@nlohmann
Copy link
Owner

nlohmann commented May 9, 2017

The first member as alias for key() is not a problem. But I currently cannot find a way to get the second member to work as alias for value(). This is my current approach which fails when trying to compile the unit tests: f98d78b.

Any help is greatly appreciated!

@Type1J
Copy link
Contributor

Type1J commented May 10, 2017

That solution makes you pay the copy cost of the key string even if you were just going to call .key(), which wouldn't use it. I think I know a better way. I'll make a PR for it.

@nlohmann
Copy link
Owner

@Type1J, your solution is much better than mine. I totally forgot about implicit conversions. Once Travis completes, I shall merge #578.

@nlohmann nlohmann removed state: help needed the issue needs help to proceed state: please discuss please discuss the issue or vote for your favorite option labels May 10, 2017
@nlohmann nlohmann self-assigned this May 10, 2017
@nlohmann
Copy link
Owner

I need to update the documentation, so I reopened the issue.

@nlohmann nlohmann reopened this May 10, 2017
@nlohmann
Copy link
Owner

There is still an issue in the code, see #578.

@nlohmann
Copy link
Owner

The test cases succeed now, but only because they just use operator== and operator!= which were added in #579. However, first and second are not fully behaving like key() and value(), respectively.

Consider this example:

#include "json.hpp"

using json = nlohmann::json;

int main()
{
    json j_object = {{"one", 1}, {"two", 2}};

    for (auto& x : json::iterator_wrapper(j_object))
    {
        std::cout << "key: " << x.first << ", value: " << x.second << '\n';
    }
}

It does not compile with Clang or GCC:

examples/iterator_wrapper.cpp:14:30: error: invalid operands to binary
      expression ('basic_ostream<char, std::__1::char_traits<char> >' and
      'iterator_key_property<nlohmann::basic_json<std::map, std::vector,
      std::__1::basic_string<char>, bool, long long, unsigned long long, double,
      std::allocator,
      adl_serializer>::iteration_proxy<nlohmann::basic_json<std::map,
      std::vector, std::__1::basic_string<char>, bool, long long, unsigned long
      long, double, std::allocator,
      adl_serializer>::iter_impl<nlohmann::basic_json<std::map, std::vector,
      std::__1::basic_string<char>, bool, long long, unsigned long long, double,
      std::allocator, adl_serializer> > >::iteration_proxy_internal>')
        std::cout << "key: " << x.first << ", value: " << x.second << '\n';
        ~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~

I would like to be able to have first and second be a drop-in replacement for key() and value(), because errors like the one above are confusing and hard to explain.

@Type1J
Copy link
Contributor

Type1J commented May 13, 2017

Maybe we should take it out. It's strange that the implicit conversion works for some, but not all compilers.

@nlohmann nlohmann added the state: help needed the issue needs help to proceed label May 15, 2017
@nlohmann
Copy link
Owner

I reverted the last PRs. It seems we need a new idea or we need to close this as "won't fix".

@Type1J
Copy link
Contributor

Type1J commented May 16, 2017

I'm having a change of heart on this one. I think "won't fix" is the right course of action. Using .key() and .value() vs .first and .second indicate better that something is being done to fetch what you requested. There could be a conversion to std::pair<key_type, value_type>, which might let you write something like

for (std::pair<std::string, json> p : a_json_object)
{
   std::cout << p.first << ": " << p.second << std::endl;
}

, but it would require you to use an explicit std::pair type. Note also that it's a value type, not a reference, so you're paying a copy cost.

I think just using .key() and .value() may be best.

@nlohmann nlohmann added the solution: wontfix the issue will not be fixed (either it is impossible or deemed out of scope) label Jun 7, 2017
@nlohmann nlohmann removed the state: help needed the issue needs help to proceed label Jun 16, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: question solution: wontfix the issue will not be fixed (either it is impossible or deemed out of scope)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants