Skip to content

Add support for parsing chunked data #983

Description

@kou

This is similar to #854 but a different feature request. Both of #854 and this are for migrating to the json gem from yajl-ruby (in Fluentd. #854's description doesn't mention it but it's mentioned in a comment #854 (comment) ) but this proposes different API. We can't migrate from yajl-ruby with API proposed in #854.

#854 proposed parser.parse(io) like API but what we need is io.each_chunk {|chunk| parser.feed(chunk)} like API. An application wants to read data not the json gem read data. parser.parse(io) like API reads data by the json gem. Fluentd can't migrate to yajl-ruby with the API.

Fluentd that is a event based log collector reads data from multiple sockets efficiently by IO multiplexing. We may not be able to read a whole JSON (e.g. ["a", 2, true]) at a time with the IO multiplexing. We may get a whole JSON as multiple chunks (e.g. ["a", , 2, and true]). yajl-ruby can parse chunks without concatenating all of them:

yajl-ruby:

require "yajl"
parser = Yajl::Parser.new
parser.on_parse_complete = lambda {|x| pp x}
parser << "[\"a\""
parser << ", 2, "
parser << "true]" # lambda {|x| pp x} is called with ["a", 2, true]

The json gem:

require "json"
JSON.parse(["[\"a\"", ", 2, ", "true]"].join)

If an application can extract a whole JSON from an input without parsing input (*), we don't need yajl-ruby style API. But an input may have multiple JSONs (like Fluentd's use case), we need yajl-ruby style API. Because we need to parse JSON to split data in an input to JSONs for this case.

(*) For example:

  1. Only one JSON exists in an input
  2. Each JSON is delimited by new line (JSON lines)
  3. ...

API

JSON::Parser.new requires a source. So it may be better that we define a new class such as JSON::StreamParser instead of reusing existing JSON::Parser.

JSON::StreamParser accepts a chunk not a whole JSON:

parser = JSON::StreamParser.new
parser.parse_chunk("[\"a\"")
parser.parse_chunk(", 2, ")
parser.parse_chunk("true]")

Other candidate for parse_chunk: consume

We need to define callback style API to pass parsed data to application. Because one parse_chunk call may parse 0 or more JSONs. For example, if chunk is [1][2], [1] and [2] are parsed at once.

JSON::Parser accepts on_load callback. We may resuse the name for JSON::StreamParser but on_load is for JSON value not JSON. It may be better that we use different name. For example:

parser = JSON::StreamParser.new
parser.on_load = lambda {|x| pp x}
parser.parse_chunk("[\"a\"")
parser.parse_chunk(", 2, ")
parser.parse_chunk("true]") # lambda {x| pp x} is called with ["a", 2, true]
parser.parse_chunk("[1][2]") # lambda {x| pp x} is called twice with [1] and [2]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions