Как в библиотеке RapidJSON передать значение JSON массива в vector?

Рейтинг: 1Ответов: 1Опубликовано: 23.02.2023

Пишу нейросеть, веса после обучения для неё хотел хранить в файле json. Написал код, с использованием библиотеки RapidJSON. Код компилируется, хоть и с предупреждениями. Функция должна брать значения весов из json массива и передавать их уже в массив(vector) программы

#include "lib/rapidjson/document.h"
#include "lib/rapidjson/writer.h"
#include <vector>

using namespace rapidjson;

void ParseWeights(std::string path, std::vector <double> &weights, int numlayer) {
    Document document;
    document.Parse(path.c_str());

    std::string slayer = "layer" + numlayer;

    const Value& layer = document[slayer.c_str()];

    assert(layer.IsArray());
    for (size_t i = 0; i < layer.Size(); i++) 
        weights.at(i) = layer[i].GetDouble();
}

Сам json файл выглядит так:

{
    "layer1" : [0.2, 0.12, 0.56, 0.58, 0.67, 0.12, 0.12, 0.45, 0.78, 0.55],
    "layer2" : [0.3, 0.12, 0.56, 0.348, 0.673, 0.12, 0.12, 0.45, 0.8, 0.1]
}

В итоге при работе функции получаю вот такую ошибку:

main: lib/rapidjson/document.h:1154: rapidjson::GenericValue<Encoding, Allocator>::MemberIterator rapidjson::GenericValue<Encoding, Allocator>::FindMember(const rapidjson::GenericValue<Encoding, SourceAllocator>&) [with SourceAllocator = rapidjson::MemoryPoolAllocator<>; Encoding = rapidjson::UTF8<>; Allocator = rapidjson::MemoryPoolAllocator<>; MemberIterator = rapidjson::GenericMemberIterator<false, rapidjson::UTF8<>, rapidjson::MemoryPoolAllocator<> >]: Assertion IsObject() failed.

Долго разбирался, копался в документации библиотеки, но так что-либо сделать не смог

Ответы

▲ 0

Во-первых, "layer" + numlayer - это const char* + целое число. "layer" + 1, например, будет указывать на 'a' и приведет к std::string "ayer". Вам нужно использовать std::to_string(numlayer).

Во-вторых, у вас нет элементов в weights поэтому .at(i) всегда будет за пределами. Если вы хотите добавить элементы в std::vector, вы должны использовать push_back.

Я рекомендую вместо этого возвращать std::vector по значению и использовать стандартный алгоритм std::transform для преобразования JSON в std::vector.

Пример получения layer1:

#include "rapidjson/document.h"
#include "rapidjson/writer.h"

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

using namespace rapidjson;

std::vector<double> ParseWeights(std::string path, int numlayer) {
    Document document;
    document.Parse(path.c_str());

    std::string slayer = "layer" + std::to_string(numlayer);

    const Value& layer = document[slayer.c_str()];

    assert(layer.IsArray());

    std::vector<double> weights;
    weights.reserve(layer.Size());

    std::transform(layer.Begin(), layer.End(), std::back_inserter(weights),
                   [](const Value& val) { return val.GetDouble(); });

    return weights;
    // for(SizeType i = 0; i < layer.Size(); i++)
    //     weights.push_back(layer[i].GetDouble());
}

int main() {
    std::string path(R"aw({
        "layer1" : [0.2, 0.12, 0.56, 0.58, 0.67, 0.12, 0.12, 0.45, 0.78, 0.55],
        "layer2" : [0.3, 0.12, 0.56, 0.348, 0.673, 0.12, 0.12, 0.45, 0.8, 0.1]
    })aw");

    std::vector<double> weights = ParseWeights(path, 1);

    for(double d : weights) std::cout << d << ' ';
    std::cout << '\n';
}

Если path — это путь к файлу, то в rapidjson рекомендуется использовать FileReadStream вместе с потоком std::FILE. Это для получения максимальной производительности.

Пример:

#include "rapidjson/document.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/writer.h"

#include <algorithm>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

using namespace rapidjson;

auto OpenCStream(const char* path, const char* mode) {
    struct FileCloser { // A functor to close a C stream
        void operator()(std::FILE* fp) { std::fclose(fp); }
    };

    std::unique_ptr<std::FILE, FileCloser> ufp(std::fopen(path, mode));
    if(not ufp) throw std::runtime_error(std::string("could not open ") + path);
    return ufp;
}

Document ReadDocumentFromFile(const std::string& path) {
    auto ufp = OpenCStream(path.c_str(), "rb");

    char readBuffer[65536];
    FileReadStream is(ufp.get(), readBuffer, sizeof readBuffer);

    Document document;
    document.ParseStream(is);
    return document;
}

std::vector<double> ParseWeightsInLayer(const Document& document,
                                        int numlayer) {
    std::string slayer = "layer" + std::to_string(numlayer);
    const Value& layer = document[slayer.c_str()];

    if(not layer.IsArray())
        throw std::runtime_error(slayer + " is not an array");

    std::vector<double> weights;
    weights.reserve(layer.Size());

    std::transform(layer.Begin(), layer.End(), std::back_inserter(weights),
                   [](const Value& val) { return val.GetDouble(); });

    return weights;
}

int main() {
    auto document = ReadDocumentFromFile("rap.json");

    std::vector<double> weights = ParseWeightsInLayer(document, 1);

    for(double d : weights) std::cout << d << ' ';
    std::cout << '\n';
}