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

Keys when iterating over objects #67

Closed
mlund opened this issue May 2, 2015 · 19 comments
Closed

Keys when iterating over objects #67

mlund opened this issue May 2, 2015 · 19 comments

Comments

@mlund
Copy link

mlund commented May 2, 2015

I'm trying to iterate over a json input file containing an unknown number of molecules (see below). For each of these the key is the name which I currently have found no way to extract. Is this possible?

Code:

#include <faunus/json.hpp>
#include <iostream>
#include <fstream>

/*
 * minimal.json:
 * {
 *   "moleculelist" : { 
 *     "salt" : { "atoms":"Na Cl", "atomic":true, "Ninit":1 },
 *     "water" : { "atoms":"H H O", "atomic":false, "Ninit":55 }
 *   }
 * }
 */

using json = nlohmann::json;

int main() {
  std::ifstream f("minimal.json");
  if (f) {
    json j;
    j << f;
    if ( ! j["moleculelist"].empty() )
      for ( auto &mol : j["moleculelist"] )
        if ( mol.is_object() ) {
          // how to get entry keys? (i.e. "salt", "water", etc.)
          for ( auto &i : mol )
            std::cout << i << "\n";
        }
  }
}
@mlund
Copy link
Author

mlund commented May 2, 2015

Found one solution,

for ( auto &mol : j["moleculelist"].get<json::object_t>() ) { ... }

which returns a std::map where first and second represents name and value. Don't know if this extra complication warrants a name() function in basic_json or not.

@nlohmann
Copy link
Owner

nlohmann commented May 3, 2015

Unfortunately, there is no way for a JSON value to "know" whether it is stored in an object, and if so under which key. All you can do is to use the json::iterator class which has a key() and value() member function to access the key and the value when you iterate over an object.

For your code:

for (auto it = j["moleculelist"].begin(); it != j["moleculelist"].end(); ++it)
{
    std::cout << it.key() << " | " << it.value() << "\n";
}

(Here, it would be of type json::iterator.) This prints

salt | {"Ninit":1,"atomic":true,"atoms":"Na Cl"}
water | {"Ninit":55,"atomic":false,"atoms":"H H O"}

I hope this helps!

@nlohmann nlohmann closed this as completed May 3, 2015
@mlund
Copy link
Author

mlund commented May 3, 2015

This will work nicely, thanks!

@wpalfi
Copy link

wpalfi commented Sep 5, 2018

for ( auto it: j["moleculelist"].items() )
{
    std::cout << it.key() << " | " << it.value() << "\n";
}

@Tudyx
Copy link

Tudyx commented Jan 21, 2021

for (auto& [key, value] : j["moleculelist"].items())
{
    std::cout << key << " | " << value << std::endl;
}

@gregmarr
Copy link
Contributor

@Tudyx FYI, std::cout << std::endl does a flush(), better to just use \n.

@byron-hawkins
Copy link

So I'm new to this json library, and I just wanted to let you know that It has taken me an hour to figure out how to squeeze the key names out of the iterator. It is highly opaque and non-intuitive, because all of the examples depict a scenario where the coder knows the names of all the keys (this includes examples in the doxygen, which is hard to find). It is not an obscure use case for the coder to have no idea what keys may exist in some json file. Can you please make some very crisp examples such as those given in this issue, and put them in the front page example sections? Just a suggestion, thanks.

@nlohmann
Copy link
Owner

Thanks for the feedback. Indeed, iterators and in particular the items() function is currently not mentioned in the README. Would this page have helped you @byron-hawkins https://json.nlohmann.me/features/iterators/ ?

@byron-hawkins
Copy link

Actually I did find that page and it didn't help me very much because it doesn't explicitly state the scenarios from which the key is accessible. The thing is that I never, ever use auto because I'm implementing low-level stuff where I must always know the data type, size, storage conditions, destructor scopes, and all of that. Now if I go look at the doxygen, the signature for these methods is enormous and tells me little about what the iterator is doing. Also I can't make an example based on the doxygen because the fully-qualified naming is just not usable. So I guess my original comment didn't explain all of the challenges I encountered, but in any case, it seems like key access is fairly opaque for such a pivotal component of the json format.

@nlohmann
Copy link
Owner

nlohmann commented Jun 7, 2021

Thanks for the feedback. The doxygen documentation is indeed not as nice as it could be. Would https://json.nlohmann.me/api/basic_json/items/ have been more helpful? It's unfortunately not linked on https://json.nlohmann.me/features/iterators/.

@byron-hawkins
Copy link

Yes, the items() page answers all the questions I was having at the time. But I realize most use cases have well-defined keys and probably would never use the key() function. Anyway, thanks for the great library, I was using boost ptree before and it was like running in deep sand!

@Janaki11
Copy link

Janaki11 commented Jun 18, 2021

I have trying to parse json, but when I use key property it returns "0".
Any idea why .key() is returning 0 ?
Any help is much appreciated.

myTest.json file contains following

 {
    URLs: [
            {
                "Key1": 
                {
                    "id" : "myid1",
                    "status" : "online"
                }
            },
            
          {
              "Key2": 
              {
                  "id" : "myid2",
                  "status" : "online"
              }
          }
    ]
}
nlohmann::json myJson;
//myTest.json is parsed using and myJson has the file content. This is working fine

if(myJson.count("URLs"))
{
    int numOfURLs = myJson["URLs"].size();
    //numOfURLs is correct and returns 2

    for (auto itr = responseJson["URLs"].begin(); itr != responseJson["URLs"].end(); ++itr)
    {
        std::string sURLKey = itr.key();
        //sURLKey = 0, expecting it to be key1 or key2

        auto URLJson = itr.value();
        std::string sId = URLJson["id"];
        std::string sStatus = URLJson["status"];     

    }
}

@nlohmann
Copy link
Owner

The JSON value at myJson["URLs"] is an array.

From https://json.nlohmann.me/api/basic_json/items/#notes:

When iterating over an array, key() will return the index of the element as string

@cppcooper
Copy link

for (auto& [key, value] : j["moleculelist"].items())
{
    std::cout << key << " | " << value << std::endl;
}

This actually doesn't work (for me at least). Would have loved to use structured bindings but my values are strings and the value ends up being an integer with structured bindings in my code.

@nlohmann
Copy link
Owner

Without more information other than "doesn't work", it's hard to help. What is the content of j? What do you expect? What did you see instead?

@cppcooper
Copy link

cppcooper commented Feb 24, 2022

I gave all the info I have. ie. my data is strings but structured bindings give integers. I'm of course talking of the types not anything else because why would I look at a string through the lens of an integer type

edit: perhaps this will be instructive though, my json data this was dealing with for me looked like

    "Hardlinks": {
      "/by-study-date/": "0008,0020",
      "/by-pid/": "0010,0020",
      "/by-dob/": "0010,0030"
    },

not really sure how though since this would only be known at runtime

@gregmarr
Copy link
Contributor

How are you determining that it's an integer? There is nothing in that code sample that is checking the type. Do you have more code somewhere?

@cppcooper
Copy link

cppcooper commented Feb 25, 2022

I must admit I'm relying on the IDE(clion) for this as I haven't compiled with code to check the types. It tells me it's an unsigned long

edit: so I went back and reimplemented the structured bindings in a unit test.. The IDE was telling me a false type, seems to work just fine. Even threw exceptions if I tried to use it as the wrong type. That's my bad!

@gregmarr
Copy link
Contributor

Thanks for confirming. The type of value should be nlohmann::json.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants