Skip to content

Instantly share code, notes, and snippets.

@shanna
Created May 17, 2012 05:40
Show Gist options
  • Save shanna/2716719 to your computer and use it in GitHub Desktop.
Save shanna/2716719 to your computer and use it in GitHub Desktop.
JSONPath like access to Oj JSON structs.
require 'delegate'
require 'oj'
require 'strscan'
# JSONPath like access to Oj JSON structs.
#
# @note Array access is identical to Array#[]
class Oj::Path < SimpleDelegator
class Error < StandardError; end
class MissingError < Error; end
class ParseError < Error; end
class TypeError < Error; end
attr_reader :root
def self.load string, options = {}
new Oj.load(string, {symbol_keys: true}.merge(options))
end
def initialize json, document = nil
@root = document || self
super json
end
def path path, *default
scanner = StringScanner.new(path)
context = self
while not scanner.eos?
if token = scanner.scan(/\$/)
context = root
elsif token = scanner.scan(/\./)
# noop, current context
elsif token = scanner.scan(/[a-z0-9_]+/i)
raise TypeError.new("Attempt to access Hash key :#{token} on a #{context.class} for '#{path}' position #{scanner.pos}.") \
unless context.respond_to?(:key)
unless context.key?(token.to_sym)
raise MissingError.new("Unknown Hash key :#{token} for '#{path}' position #{scanner.pos}.") if default.empty?
context = default.first
break
end
context = context[token.to_sym]
elsif token = scanner.scan(/\[([\-0-9](?:(?:(?:\.\.)|(?:\,\s))[\-0-9])?)\]/)
raise TypeError.new("Attempt to access Array #{token} on a #{context.class} for '#{path}' postion #{scanner.pos}.") \
unless context.respond_to?(:[])
context = eval("context#{token}")
if context.nil?
raise MissingError.new("Array access #{path} returned nil for '#{path}' position #{scanner.pos}.") if default.empty?
context = default.first
break
end
else
raise ParseError.new("Invalid token '#{scanner.peek(1)}' for '#{path}' position #{scanner.pos}.")
end
end
self.class.new(context, root)
end
end # Oj::Path
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment