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

Sequential reading of JSON arrays #851

Closed
gjgiezeman opened this issue Dec 1, 2017 · 10 comments
Closed

Sequential reading of JSON arrays #851

gjgiezeman opened this issue Dec 1, 2017 · 10 comments
Labels
solution: proposed fix a fix for the issue has been proposed and waits for confirmation state: stale the issue has not been updated in a while and will be closed automatically soon unless it is updated

Comments

@gjgiezeman
Copy link

gjgiezeman commented Dec 1, 2017

I have an application that logs events as an array of JSON objects. The objects are written one by one to some network stream. I would like to process them when they arrive, not after the application has closed and the array is complete.

So, I would like to have (or be able to write) a class with an interface like this:

class SeqJsonReader {
  public:
    void append(std::string const &str);
    void append(char const *str);
    void append(char const *begin, char const *end);
    bool has_value() const;
    nlohmann::json next_value();
    bool at_end() const; // true if closing ']' of array is processed.
};

Usage could be like this:

bool reader_loop(vector<string> const &msgs)
{
    SeqJsonReader reader;
    auto cur = msgs.begin();
    while (!reader.at_end() && cur!=msgs.end()) {
        try {
            reader.append(*cur++);
            while (reader.has_value()) {
                nlohmann::json val = reader.next_value();
                // do something with val
            }
        } catch (...) {
            return false;
        }
    }
    return true;
}

int main()
{
    vector<string> msgs;
    msgs.push_back(R"([{"product":"apple","amount":"512","unit":"gram"})");
    msgs.push_back(R"(,{"product":"pear")");
    msgs.push_back(R"(},42,{"product":"orange","amount":"402","unit":"gram"}])");
    reader_loop(msgs);
}

In practice the strings (msgs) would come from a recv call of a socket or something like that.

Am I right that currently this is hard to implement? It seems that I can't parse partial JSON input.

@gregmarr
Copy link
Contributor

gregmarr commented Dec 1, 2017

Are you able to change the format at all? Could you write as individual objects instead of an array? Essentially JSON Lines http://jsonlines.org/
I'm pretty sure that the latest version can handle this using some of the istream methods.

@pingsoli
Copy link

pingsoli commented Dec 4, 2017

@gregmarr I'm really sorry to bother you, I have a question about json array of string, it doesn't have a key, how can I detect the value in the array ? Following is my solution

json j = { 1, 2, 3, 4, 5 };
for (auto &e : j)
{
  if (e == 2)
  {
    // json array contains 2
    std::cout << "Found 2" << std::endl;
    // do something 
  }
}

Does their has any function like this ?

json j = { 1,  2, 3, 4, 5 };
if (j.contains(2)) 
{
  // do something 
}

@nlohmann
Copy link
Owner

nlohmann commented Dec 4, 2017

@pingsoli You can use std::find (http://en.cppreference.com/w/cpp/algorithm/find), just like you would for a std::vector.

@pingsoli
Copy link

pingsoli commented Dec 4, 2017

@nlohmann I appreciate your reply, thank you very much. Your reply is really fast, thanks for your replay again. Have a good day. : )

@gjgiezeman
Copy link
Author

gjgiezeman commented Dec 4, 2017

Gregg, thank you for your suggestion. I'll see if I can convince upstream to change the format.

@nlohmann
Copy link
Owner

nlohmann commented Dec 4, 2017

You could try to use a callback function which you can pass to the parse function, see https://nlohmann.github.io/json/classnlohmann_1_1basic__json_aecae491e175f8767c550ae3c59e180e3.html#aecae491e175f8767c550ae3c59e180e3:

#include <iostream>
#include "json.hpp"

using json = nlohmann::json;

int main(int argc, char* argv[]) {
    json x = {1,2,3,4,5,6,7,8,9};
    std::string s = x.dump();

    json::parser_callback_t cb = [](int depth, json::parse_event_t event, json & parsed)
    {
        if (event == json::parse_event_t::value)
        {
            std::cout << parsed << std::endl;
            return false;
        }
        else
        {
            return true;
        }
    };

    
    json y = json::parse(s, cb);
    
    std::cout << y << std::endl;
}

The example shows how to process values individually (here: print to std::cout) and not store them (hence returning false). As a result, the last line returns null, because no value has been created with the parse call. The signature and the approach is different from the one you proposed, but I think you can build on it.

@nlohmann nlohmann added the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Dec 4, 2017
@nlohmann
Copy link
Owner

Can I close this issue?

@ataber
Copy link

ataber commented Dec 12, 2017

Is there any way to do the callback-based parsing while reading from an ifstream incrementally?

@nlohmann
Copy link
Owner

I am afraid this is not possible in the moment. There is a private API, but it is not exposed publicly so far.

@stale
Copy link

stale bot commented Jan 22, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the state: stale the issue has not been updated in a while and will be closed automatically soon unless it is updated label Jan 22, 2018
@stale stale bot closed this as completed Jan 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
solution: proposed fix a fix for the issue has been proposed and waits for confirmation state: stale the issue has not been updated in a while and will be closed automatically soon unless it is updated
Projects
None yet
Development

No branches or pull requests

5 participants