0

我一直在扯头发(不过一开始并不多),试图找出一种方法来访问 Web 服务,然后解析和反序列化 json 块,同时解析到我的对象中而不存储整个文档(500mb+)。实际上,我正在尝试使用 SAX 风格的解析将 libcurl 和 rapidjson 连接到我的类中。

也许我在想这个错误,但我目前的想法是在 curl_writeback 期间将接收到的数据添加到 json 文本流中。我正在努力的是如何启动然后让 rapidjson 解析引擎等待来自 curl 写回的下一个数据块。在处理未完成的令牌之前暂停,直到接收到新的流缓冲区。我不想附加缓冲区,因为我想释放以前的缓冲区。

有没有人这样做过?

这里有一些代码来说明这个概念......但显然不完整(而且可能全错了!)

这是将信息传递给 curl_write 的结构,以便它知道如何对解析器进行回调。

struct CurlHandler {
    stringstream *data;
    Reader *reader;
    void *parsehandler;
};

然后在发起请求之前,我填充结构如下:

rapidjson::Reader reader;
CurlHandler handler;
ParseHandler parser;
handler.reader = &reader;
handler.parsehandler = &parser;
SetOption(CURLOPT_WRITEDATA, (void *) &handler);

ParseHandler 是 rapidjson 进行 SAX 解析所需的类。这个想法是在 curl_write 期间调用 rapidjson 解析器(以某种方式):

static size_t curl_write(void *ptr, size_t size, size_t nmemb, void *CurlHandlerPtr) {
    CurlHandler *handler = (CurlHandler *) CurlHandlerPtr;

//  handler->reader.Parse(handler->data, handler->parsehandler);   ????????

    return 1;
}

我当然希望有比这更简单的答案。

这是上下文的完整代码:

#pragma once
#include <string>
#include <iostream>
#include "curl\curl.h"
#include "rapidjson\rapidjson.h"
#include "rapidjson\reader.h"

using namespace std;
using namespace rapidjson;

struct CurlHandler {
    stringstream *data;
    Reader *reader;
    void *parsehandler;
};

static size_t curl_write(void *ptr, size_t size, size_t nmemb, void *CurlHandlerPtr) {
    CurlHandler *handler = (CurlHandler *) CurlHandlerPtr;

//  handler->reader.Parse(handler->data, handler->parsehandler);   ????????

    return 1;
}


class ParseHandler {
    bool Null() { cout << "Null()" << endl; return true; }
    bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; }
    bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; }
    bool Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; return true; }
    bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; }
    bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; }
    bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; }
    bool String(const char* str, SizeType length, bool copy) {
        cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
        return true;
    }
    bool StartObject() { cout << "StartObject()" << endl; return true; }
    bool Key(const char* str, SizeType length, bool copy) {
        cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
        return true;
    }
    bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
    bool StartArray() { cout << "StartArray()" << endl; return true; }
    bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
};

class StreamTest {
private:
    string _username;
    string _password;

    CURL *_curl;
    CURLcode _error;

public:
    StreamTest() {
        curl_global_init(CURL_GLOBAL_ALL);
        _curl = curl_easy_init();
    };
    ~StreamTest() {
        curl_global_cleanup();
    };

    int Initialize(string username, string password) {
        _username = username;
        _password = password;
        return 0;
    }

    int Get(std::string url) {
        CURLcode result;

        rapidjson::Reader reader;
        CurlHandler handler;
        ParseHandler parser;
        handler.reader = &reader;
        handler.parsehandler = &parser;

        std::string s = _username; s += ":"; s += _password;

        SetOption(CURLOPT_USERPWD, s.c_str());
        SetOption(CURLOPT_URL, url.c_str());
        SetOption(CURLOPT_WRITEFUNCTION, curl_write);
        SetOption(CURLOPT_WRITEDATA, (void *) &handler);
        result = curl_easy_perform(_curl);

        return 0;
    }

    template <typename ValueType>
    StreamTest& SetOption(CURLoption opt, ValueType val) {
        if(_curl && _error == CURLE_OK) {
            _error = curl_easy_setopt(_curl, opt, val);
        }
        return *this;
    }
};

int _tmain(int argc, _TCHAR* argv[]) {
    StreamTest http;

    http.Initialize("guest", "passwd");

    http.Get("http://localhost/GetData");

    cin.get();
}

编辑:

理想情况下,_tmain 中的 http.Get 函数将传入对要反序列化的对象的引用。该对象将在各种回调中接收来自 rapidjson 的所有 SAX 事件。

4

2 回答 2

0

RapidJSON 的设计本身不支持解析期间的暂停/恢复。因为这会为备份/恢复解析状态增加很多开销。

但是,上面提到的需求可以通过使用 Libcurl 的Multi interface来解决,它实际上使用了多线程。

libcurl 中的fopen.c示例显示了如何将 HTTP GET 模拟为标准 FILE I/O API。使用此示例应该可以方便地修改rapidjson::FileReadStream以支持从 HTTP 解析 JSON。

一般来说,开发者在使用 RapidJSON 时可能会使用多线程来解决这类问题。

于 2015-01-24T15:31:50.463 回答
0

这是概念:创建一个具有读写端的匿名管道。在您传递给 的回调中WRITEFUNCTION,将从 curl 收到的字节写入管道的写入端。然后在另一个线程中,不是将rapidjson::StringStream对象传递给,而是传递从管道的读取端创建reader.Parse<>()的对象。rapidjson::FileReadStream当您的回调写入管道时,您的 SAX 阅读器读取并解析管道中的数据。

于 2021-01-02T19:46:52.973 回答