Skip to content

Instantly share code, notes, and snippets.

@micahboyd
Last active December 30, 2023 21:13
Show Gist options
  • Save micahboyd/55e42e49a66422d55f73277244fb5b17 to your computer and use it in GitHub Desktop.
Save micahboyd/55e42e49a66422d55f73277244fb5b17 to your computer and use it in GitHub Desktop.
JSON #parse object_class
# Capture JSON into custom object similar to OpenStruct
class JsonClass
def []=(key, value)
instance_variable_set("@#{key}", new_member!(key, value))
end
def [](key)
instance_variable_get("@#{key}")
end
def new_member!(key, value)
name = value.to_sym
unless singleton_class.method_defined?(name)
define_singleton_method(name) { instance_variable_get("@#{name}") }
define_singleton_method("#{name}=") { |v| instance_variable_set("@#{name}", v) }
end
value
end
end
json = '{"a":"a","b":"b"}'
parsed_json = JSON.parse(json, object_class: JsonClass)
# getting values
parsed_json[:a] # => 'a'
parsed_json.a # => 'a'
# updating values
parsed_json.b # => 'b'
parsed_json[:b] = 'b2'
parsed_json.b # => 'b2'
# setting new keys
parsed_json[:c] = 'c'
parsed_json.c # => 'c'
parsed_json.d = 'd' # => NoMethodError :/
# Stricter object without setters or value mutations
class StrictJsonClass
def [](key)
return unless instance_variable_defined?("@#{key}")
send(key)
end
def members
instance_variables.map { |v| v.to_s.delete_prefix('@').to_sym }
end
def values
instance_variables.map { |v| instance_variable_get(v) }
end
def to_h
freeze_hash(members.zip(values).to_h)
end
alias attributes to_h
private
def []=(key, value)
defined = define_getter(key)
instance_variable_set("@#{key}", value) if defined
end
def define_getter(name)
return if singleton_class.method_defined?(name, false)
define_singleton_method(name) { instance_variable_get("@#{name}") }
end
def freeze_hash(hash)
hash.transform_values(&:freeze).freeze
end
end
json = '{"a":"1","b":"2"}'
strict_json = JSON.parse(json, object_class: StrictJsonClass)
# getting values
strict_json[:a] # => "1"
strict_json.a # => "1"
# getting attributes
strict_json.attributes # => {:a=>"1", :b=>"2"}
# can't modifiy attributes
strict_json.attributes[:b] = '3' # => FrozenError: frozen Hash
strict_json.attributes.merge!(c: '3') # => FrozenError: frozen Hash
strict_json.attributes[:b] << '3' # => FrozenError: frozen String
# can't update values
strict_json.b = '3' # => NoMethodError: undefined method
strict_json[:b] = '3' # => NoMethodError: private method
strict_json.b # => "2"
# can't set new keys
strict_json[:c] = '3' # => NoMethodError: private method
strict_json.c = '3' # => NoMethodError: undefined method
strict_json[:c] # => nil
strict_json.c # => NoMethodError: undefined method
# OpenStruct but able to handpick initialization data
class JsonStruct < OpenStruct
ATTRIBUTES = %w[a b]
def []=(key, value)
return unless ATTRIBUTES.include?(key)
super
end
end
json = '{"a":"1","c":"2"}'
json_struct = JSON.parse(json, object_class: JsonStruct)
# getting, setting values (all OpenStruct behaviour)
json_struct[:a] # => "1"
json_struct.a = '2' # => "2"
# only sets specifed attributes
json_struct.c # => nil
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment