Получение вложенного объекта внутри строки JSON с использованием rapidjson

Мне нужно получить вложенный объект внутри строки JSON, и я пытаюсь сделать это с помощью rapidjson. Все, что я нашел, это то, как извлекать массивы и базовые типы, но не подобъекты. Я создал следующий игрушечный пример, который выдает ошибку:

rapidjson::Document document;
std::string test =  " { \"a\": { \"z\" : 21 } } ";
std::cout << test << std::endl;
if ( document.Parse<0>( test.c_str() ).HasParseError() ) {
    std::cout << "Parsing error" << std::endl;
} else {
    if ( document[ "a" ].IsObject() ) {
        std::cout << "OK" << std::endl;
        std::cout << document[ "a" ].GetString() << std::endl;
    }
}

Это вывод при выполнении:

{ "a": { "z" : 21 } } 
OK
JSONTest: ../rapidjson/document.h:441: const typename Encoding::Ch* rapidjson::GenericValue<Encoding, Allocator>::GetString() const [with Encoding = rapidjson::UTF8<char>, Allocator = rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>]: Assertion `IsString()' failed. Aborted

Как мне получить внутренний объект, чтобы продолжить синтаксический анализ? Спасибо.

Изменить: мне нужно получить строковое представление внутреннего объекта, чтобы я мог вызвать другую функцию, которая будет его анализировать.

Правка 2: код, позволяющий получить внутренний объект в виде строки:

rapidjson::Document document;
std::string test =  "{\"a\":{\"z\":21}} ";
if ( document.Parse<0>( test.c_str() ).HasParseError() ) {
    std::cout << "Error parsing" << std::endl;
} else {
    if ( document[ "a" ].IsObject() ) {
        rapidjson::StringBuffer sb;
        rapidjson::Writer<rapidjson::StringBuffer> writer( sb );
        document[ "a" ].Accept( writer );
        std::cout << sb.GetString() << std::endl;
    }
}

person pparescasellas    schedule 05.10.2012    source источник
comment
Означает ли это, что RapidJson не поддерживает иерархические объекты? так это только разбирать корневой уровень?!?!   -  person Paolo Vigori    schedule 01.03.2017
comment
Да, edit2 очень полезен для рекурсии, может преобразовать внутренний объект json в строку, которую можно рекурсивно анализировать.   -  person Paulus    schedule 26.02.2020


Ответы (8)


Вам нужно перебирать элементы объекта вручную, так как GetString() работает только с элементами строки, а document["a"] является объектом. Вам нужно пройтись по членам этого объекта, используя переменную MemberIterator. У меня не было практики в C* более 15 лет, поэтому могу дать только общее представление о том, как это должно работать:

for (MemberIterator m = document["a"].MemberBegin(); m != document["a"].MemberEnd(); ++m) {
    std::cout << m.name << " " << (m.IsNumber()?m.GetNumber():m.GetString()) << endl;
}

Кроме того, вы можете взглянуть на метод Accept(), похоже, он возвращает строку JSON объекта, который вы ему даете.

person Vesper    schedule 05.10.2012
comment
Да, Accept() был ответом. Это позволяет поместить внутренний объект в модуль записи, который заполняет строковый буфер. Затем можно получить строку из буфера. Я отредактировал исходный вопрос с рабочим кодом. Большое спасибо! - person pparescasellas; 05.10.2012

Если элемент является объектом, вы можете просто получить доступ к подсвойствам с помощью []:

for (SizeType i = 0; i < layers.Size(); i++){   
  cout << layers[i]["name"].GetString() << endl;
}
person lasote    schedule 30.07.2014

В Rapidjson реализован еще один отличный подход — указатели JSON. Они имеют экспериментальный статус и согласно документации должны быть включены в v.1.1. В любом случае этот подход выглядит как XPATH для XML, поэтому для получения вложенного значения мы можем использовать такой синтаксис

Value* tmpValue = GetValueByPointer(doc, "/result/job/blob");

Я попробовал этот функционал и, на мой взгляд, это лучше, чем итераторы.

person Kostia Shiian    schedule 22.07.2018

Вы можете использовать указатель для получения подобъекта:

Value& a = *GetValueByPointer(document, "/a");
int z = a["z"].GetInt();
person Michael    schedule 14.08.2018
comment
Добро пожаловать в Stack Overflow! Пожалуйста, не отвечайте только исходным кодом. Постарайтесь дать хорошее описание того, как работает ваше решение. См.: Как написать хороший ответ?. Спасибо - person sɐunıɔןɐqɐp; 14.08.2018

GenericObject doc2 = document["a"].GetObjectW();
int z = doc2["z"].GetInt();    

простой способ разобрать вложенные объекты внутри json с помощью RapidJson lib для c++..

person Kvae Kvae22    schedule 28.01.2021

Вот один пример кода для получения вложенного объекта как объекта rapidjson::Document.

Document get_nested(Document &d, std::string key){
rapidjson::StringBuffer buffer;
const char *key_ctr = key.c_str();

assert(d[key_ctr].IsObject());

rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
d[key_ctr].Accept(writer);

rapidjson::Document result;
rapidjson::StringStream s(buffer.GetString());
result.ParseStream(s);

return result;
}
person RealityPC    schedule 05.10.2015

вы также можете использовать указатель документа:

Document *document= new Document();
document->parse( test.c_str());

и поместите указатель значения и используйте его

Value *val= document;
val = &(*val)["a"];
val = &(*val)["z"];
cout << val->GetString();
person Shubham Trigunait    schedule 07.10.2016

Это то, над чем я недавно работал:

void enter(const Value &obj, size_t indent = 0) { //print JSON tree

if (obj.IsObject()) { //check if object
    for (Value::ConstMemberIterator itr = obj.MemberBegin(); itr != obj.MemberEnd(); ++itr) {   //iterate through object   
        const Value& objName = obj[itr->name.GetString()]; //make object value

        for (size_t i = 0; i != indent; ++i) //indent
            cout << " ";

        cout << itr->name.GetString() << ": "; //key name

        if (itr->value.IsNumber()) //if integer
            std::cout << itr->value.GetInt() ;

        else if (itr->value.IsString()) //if string
            std::cout << itr->value.GetString();


        else if (itr->value.IsBool()) //if bool
            std::cout << itr->value.GetBool();

        else if (itr->value.IsArray()){ //if array

            for (SizeType i = 0; i < itr->value.Size(); i++) {
                if (itr->value[i].IsNumber()) //if array value integer
                    std::cout << itr->value[i].GetInt() ;

                else if (itr->value[i].IsString()) //if array value string
                    std::cout << itr->value[i].GetString() ;

                else if (itr->value[i].IsBool()) //if array value bool
                    std::cout << itr->value[i].GetBool() ;

                else if (itr->value[i].IsObject()){ //if array value object
                    cout << "\n  ";
                    const Value& m = itr->value[i]; 
                    for (auto& v : m.GetObject()) { //iterate through array object
                        if (m[v.name.GetString()].IsString()) //if array object value is string
                            cout << v.name.GetString() << ": " <<   m[v.name.GetString()].GetString();
                        else //if array object value is integer
                            cout << v.name.GetString() << ": "  <<  m[v.name.GetString()].GetInt();

                       cout <<  "\t"; //indent
                    }
                }
                cout <<  "\t"; //indent
            }
        }

        cout << endl; 
        enter(objName, indent + 1); //if couldn't find in object, enter object and repeat process recursively 
    }     
}
}

Это может обрабатывать любой тип дерева JSON. Все, что вам нужно сделать, это передать значение как таковое:

Value v = document.GetObject();
Value& m= v;
enter(m);

И вы сделали!

person a.raya203    schedule 30.03.2017
comment
Вы просто вставляете свой ответ на все вопросы Rapidjson? отрицательный голос - person Gillis Haasnoot; 01.04.2017
comment
Я искал в Интернете целую вечность для этого и не мог найти его. Просто пытаюсь помочь. Успокоиться - person a.raya203; 05.04.2017