Let JSON::PullParser#on_key work with nested keys

Hi,

Sometimes while reading JSON files I find using JSON::Serializable very verbose depending on the JSON structure. So I would like a dig like method to just go to the deeps of the nested JSON objects and get what I want but trying to allocate not much memory like JSON.parse.

A dummy example: reading this JSON where I want the value of the ! key, i.e. 42:

{"hey": { "ho": { "!": 42 } } }

I would like something like:

input = %({"hey": { "ho": { "!": 42 } } })

parser = JSON::PullParser.new(input)
parser.on_key("hey", "ho", "!") do
  puts parser.read_int
end

Instead of writing a lot of nested classes that implement JSON::Serializable or writting a lot of nested blocks using JSON::PullParser just to deal with a verbose JSON structure.

A monkey patch that make the code above work is show bellow

require "json"

class JSON::PullParser
  private def on_key(keys : Indexable, idx : Int32, &block : self -> _)
    key = keys[idx]
    on_key(key) do
      if idx == keys.size - 1
        block.call(self)
      else
        on_key(keys, idx + 1, &block)
      end
    end
  end

  def on_key(*keys, &block : self -> _)
    on_key(keys, 0, &block)
  end
end

The code above only works with nested JSON objects, maybe adding support to arrays would be nice too, i.e. if the key is a number the code assumes the user is expecting an array and interpret the number as the array index, since JSON keys are always strings by definition… :thinking:

Anyway… I find this useful for me, is it useful enough to deserve my time crafting a PR to add this to stdlib?

P.S.: I come to this trying to read the JSON output of an Elastic Search reply using JSON.parse and failing due to Int128 numbers in the JSON… so I had to use JSON::PullParser.

4 Likes

The idea sounds great and the implementation looks good.
Would you mind posting a concrete proposal to the issue tracker?

Yes, then work on a PR that also works with arrays