C++ nlohmann json - как перебрать / найти вложенный объект
Я пытаюсь перебрать вложенный json, используя nlohmann::json. Мой объект JSON ниже:
{
"one": 1,
"two": 2
"three": {
"three.one": 3.1
},
}
Я пытаюсь перебрать и / или найти вложенные объекты. Но, похоже, поддержки по умолчанию нет. Похоже, мне приходится перебирать каждый подобъект, создавая другой цикл, или рекурсивно вызывать fn для каждого подобъекта.
Следующий фрагмент кода и его результат показывают, что возможна только итерация верхнего уровня.
void findNPrintKey (json src, const std::string& key) {
auto result = src.find(key);
if (result != src.end()) {
std::cout << "Entry found for : " << result.key() << std::endl;
} else {
std::cout << "Entry not found for : " << key << std::endl ;
}
}
void enumerate () {
json j = json::parse("{ \"one\" : 1 , \"two\" : 2, \"three\" : { \"three.one\" : 3.1 } } ");
//std::cout << j.dump(4) << std::endl;
// Enumerate all keys (including sub-keys -- not working)
for (auto it=j.begin(); it!=j.end(); it++) {
std::cout << "key: " << it.key() << " : " << it.value() << std::endl;
}
// find a top-level key
findNPrintKey(j, "one");
// find a nested key
findNPrintKey(j, "three.one");
}
int main(int argc, char** argv) {
enumerate();
return 0;
}
и вывод:
ravindrnathsMBP:utils ravindranath$ ./a.out
key: one : 1
key: three : {"three.one":3.1}
key: two : 2
Entry found for : one
Entry not found for : three.one
Итак, есть ли рекурсивная итерация, или мы должны сделать это сами, используя метод is_object()?
3 ответа
Действительно, итерация не повторяется, и для этого пока нет библиотечной функции. Как насчет:
#include "json.hpp"
#include <iostream>
using json = nlohmann::json;
template<class UnaryFunction>
void recursive_iterate(const json& j, UnaryFunction f)
{
for(auto it = j.begin(); it != j.end(); ++it)
{
if (it->is_structured())
{
recursive_iterate(*it, f);
}
else
{
f(it);
}
}
}
int main()
{
json j = {{"one", 1}, {"two", 2}, {"three", {"three.one", 3.1}}};
recursive_iterate(j, [](json::const_iterator it){
std::cout << *it << std::endl;
});
}
Выход:
1
"three.one"
3.1
2
Это ответвление от принятого ответа, которое дает вам дополнительное преимущество наличия родительских ключей (в дополнение к итератору), когда вы «прогуливаетесь» по дереву json.
Родительские ключи предоставляются в формате списка, чтобы их можно было легко перебирать напрямую. Я также предоставил средства для преобразования этого списка строк во «вложенный ключ json» (т.е.
Вспомогательные функции
#include <string>
#include <list>
#include "nlohmann.hpp"
using JsonIter = nlohmann::json::const_iterator;
typedef std::list<std::string> JsonKeys;
std::string toJsonStringKey( const JsonKeys &keys )
{
static const std::string JSON_KEY_DELIM( "/" );
std::string s;
for( auto k : keys ) s.append( JSON_KEY_DELIM + k );
return s;
}
nlohmann::json::json_pointer toJsonPointerKey( const JsonKeys &keys )
{ return nlohmann::json::json_pointer( toJsonStringKey( keys ) ); }
nlohmann::json::json_pointer toJsonPointerKey(
const JsonKeys &parentKeys, JsonIter it )
{
JsonKeys allKeys( parentKeys );
allKeys.push_back( it.key() );
return nlohmann::json::json_pointer( toJsonStringKey( allKeys ) );
}
typedef std::function< void( const JsonKeys &parentKeys,
nlohmann::json::const_iterator it )> WalkJsonCallBack;
void walkJson( const nlohmann::json &jsonObj, JsonKeys &parentKeys,
WalkJsonCallBack callback )
{
for( auto it( jsonObj.begin() ); it != jsonObj.end(); ++it )
{
if( it->is_structured() )
{
parentKeys.push_back( it.key() );
walkJson( *it, parentKeys, callback );
parentKeys.pop_back();
}
else callback( parentKeys, it );
}
}
Пример реализации
const nlohmann::json parsed( nlohmann::json::parse( raw ) );
JsonKeys parentKeys;
walkJson( parsed, parentKeys, []( const JsonKeys &parentKeys, JsonIter it )
{
// INSERT YOUR CODE HERE
// Example of getting a pointer key..
const auto key( toJsonPointerKey( parentKeys, it ) );
// Now, do whatever with that key...
});
Образец данных
А вот пример данных операции после добавления еще нескольких полей и вложений:
const std::string testData(
"{ \"one\" : 1 , \"two\" : 2, "
"\"three\" : { "
" \"three.one\" : 3.1, "
" \"three.two\" : { \"three.two.one\" : 3.21, \"three.two.two\" : 3.22 }, "
" \"three.three\": { \"three.three.one\" : 3.31, \"three.three.two\" : 3.32 }, "
" \"three.four\": 3.4, "
" \"three.five\": { \"three.five.one\" : 3.51, \"three.five.two\" : 3.52 } "
"}, "
"\"four\" : 4"
"} " );
Основываясь на предыдущих ответах, это проверяется:
typedef std::list<std::string> json_keys;
typedef std::function< void(const json_keys& key, const json& it)> json_recursive_iterate_callback;
void json_recursive_iterate(const json& js, json_keys& keys, json_recursive_iterate_callback cb)
{
for (auto& [key, val] : js.items())
{
keys.push_back(key);
if (val.is_structured())
json_recursive_iterate(val, keys, cb);
else
cb(keys, val);
keys.pop_back();
}
}
Используйте это следующим образом:
json_keys keys;
json_recursive_iterate(body, keys, [&](const json_keys& keys, const json& it)
{
std::string key;
for (auto k : keys) key.append("/" + k);
std::cout << key << " = " << it << std::endl;
});