Ruby JSON node select, view & value change [duplicate] - ruby

This question already has an answer here:
How to convert JSON to a hash, search for and change a value
(1 answer)
Closed 7 years ago.
I am trying to select a node from a JSON parsed file and display the node/key hierarchy by using dots to separate the nodes.
json_hash =
{
"Samples": {
"Locations": {
"Presets": "c:\\Presets\\Inst",
"Samples": "c:\\Samples\\seperates\\round_robin"
},
"Format": {
"Type": "AIFF",
"Samplerate": 48,
"Bitrate": 24
},
"Groupings": {
"Type": "dynamic",
"Sets": {
"Keyswitch": "enabled",
"Mods": "Enabled"
}
},
"Ranges": {
"Keys": {
"LowKey": 30,
"HighKey": 80,
"LowVel": 0,
"MidVel": 53,
"HighVel": 127
}
},
"Dynamics": {
"DN": {
"MultiRange": "enabled",
"Range": [
"0-36",
"37-80"
]
}
}
},
"Modulation": {
"Pitch": true,
"Range": []
},
"Encoded": false,
"test": "test_one"
}
My idea is to output something like this;
Samples.Locations.Presets => c:\Presets\Inst
Samples.Locations.Samples => c:\Samples\seperates\round_robin
I managed to get this;
Presets. => c:\Presets\Inst
Presets.Samples. => c:\Samples\seperates\round_robin
Using this code:
def node_tree(hash)
kl = ''
hash.each do |k, v|
kl << "#{k}."
if v.kind_of?(Hash)
node_tree(v)
else
print "#{kl} => "
print "#{Array(v).join(', ')}\n"
end
end
end
node_tree(json_hash)
It would be great if I could get the full node hierarchy to display and then be able to select a node using dot separated node and change the value.
So I could change the Samples.Dynamics.DN.Range from 0-36, 37-80 to 0-30, 31-60, 61-90
key = 'Samples.Dynamics.DN.Range'
value = %w(0-30 31-60 61-90)
node_set('Samples.Dynamics.DN.Range', value)
I can't work on the later until I figure out how best to display and select the nodes and values.

Using this method you can flatten the hash:
def flat_hash(hash, k = [])
return { k => hash } unless hash.is_a?(Hash)
hash.inject({}) { |h, v| h.merge! flat_hash(v[-1], k + [v[0]]) }
end
p flat_hash(json_hash)
#=> {["Samples", "Locations", "Presets"]=>"c:\\Presets\\Inst", ["Samples", "Locations", "Samples"]=>"c:\\Samples\\seperates\\round_robin", ["Samples", "Format", "Type"]=>"AIFF", ["Samples", "Format", "Samplerate"]=>48, ["Samples", "Format", "Bitrate"]=>24, ["Samples", "Groupings", "Type"]=>"dynamic", ["Samples", "Groupings", "Sets", "Keyswitch"]=>"enabled", ["Samples", "Groupings", "Sets", "Mods"]=>"Enabled", ["Samples", "Ranges", "Keys", "LowKey"]=>30, ["Samples", "Ranges", "Keys", "HighKey"]=>80, ["Samples", "Ranges", "Keys", "LowVel"]=>0, ["Samples", "Ranges", "Keys", "MidVel"]=>53, ["Samples", "Ranges", "Keys", "HighVel"]=>127, ["Samples", "Dynamics", "DN", "MultiRange"]=>"enabled", ["Samples", "Dynamics", "DN", "Range"]=>["0-36", "37-80"], ["Modulation", "Pitch"]=>true, ["Modulation", "Range"]=>[], ["Encoded"]=>false, ["test"]=>"test_one"}
Using regular expression for valid path /([A-Z|a-z]:\\[^*|"<>?\n]*)|(\\\\.*?\\.*)/ you can filter flatten hash:
VALID_PATH_REGEXP = /([A-Z|a-z]:\\[^*|"<>?\n]*)|(\\\\.*?\\.*)/
flat_hash(json_hash).each_pair do |k, v|
puts "#{Array(k).join('.')} => #{v}" if v.to_s =~ VALID_PATH_REGEXP
end
#=> Samples.Locations.Presets => c:\Presets\Inst
#=> Samples.Locations.Samples => c:\Samples\seperates\round_robin
Final code:
require 'json'
VALID_PATH_REGEXP = /([A-Z|a-z]:\\[^*|"<>?\n]*)|(\\\\.*?\\.*)/
json_hash = JSON.parse(File.read('example.json'))
def flat_hash(hash, k = [])
return { k => hash } unless hash.is_a?(Hash)
hash.inject({}) { |h, v| h.merge! flat_hash(v[-1], k + [v[0]]) }
end
flat_hash(json_hash).each_pair do |k, v|
puts "#{Array(k).join('.')} => #{v}" if v.to_s =~ VALID_PATH_REGEXP
end
#=> Samples.Locations.Presets => c:\Presets\Inst
#=> Samples.Locations.Samples => c:\Samples\seperates\round_robin
Update:
That's probably better to return hash with the results, rather than just printing them:
require 'json'
VALID_PATH_REGEXP = /([A-Z|a-z]:\\[^*|"<>?\n]*)|(\\\\.*?\\.*)/
json_hash = JSON.parse(File.read('example.json'))
def flat_hash(hash, k = [])
return { k => hash } unless hash.is_a?(Hash)
hash.inject({}) { |h, v| h.merge! flat_hash(v[-1], k + [v[0]]) }
end
def node_tree(hash)
flat_hash(hash).map { |k, v| [Array(k).join('.'), v] if v.to_s =~ VALID_PATH_REGEXP }.compact.to_h
end
p node_tree(json_hash)
#=> {"Samples.Locations.Presets"=>"c:\\Presets\\Inst", "Samples.Locations.Samples"=>"c:\\Samples\\seperates\\round_robin"}

Related

Converting a ruby hash to another collection

atts = {
key1: "value1",
key2: "value2"
}
Which should then produce the following:
custom_atts = {
"key1" => {
string_value: "value1",
data_type: "String"
},
"key2" => {
string_value: "value2",
data_type: "String"
}
}
So I want to create a function that will convert atts into custom_atts.
def custom_atts(atts)
end
I can loop through hash values like this:
h.each do |key, value|
puts key
value.each do |k,v|
puts k
puts v
end
end
But not sure how to create a hash with a hash in it, while in a loop.
Try this one:
atts = {
key1: "value1",
key2: "value2"
}
def custom_atts(atts)
Hash[ atts.keys.map { |key|
[ key, {
string_value: atts[key],
data_type: "String" } ] } ]
end
puts custom_atts(atts).inspect
# {:key1=>
# {:string_value=>"value1",
# :data_type=>"String"},
# :key2=>{
# :string_value=>"value2",
# :data_type=>"String"}}
If your new keys should be strings instead of symbols, then change key to key.to_s inside the map.
atts.each_with_object({}) { |(k,v),h| h[k.to_s] = { string_value: v, data_type: v.class } }
#=> {"key1"=>{:string_value=>"value1", :data_type=>String},
# "key2"=>{:string_value=>"value2", :data_type=>String}}

Unable to fetch deeply nested hash value

I have this rake task which uses rest-client to fetch some messy JSON from this API, and then uses hashie to make the code prettier.
Unfortunately I'm unable to fetch one of the deeply nested values, productGroup. If working correctly, it should output :category => "Jeans" or similar. Please see the JSON at the bottom.
This did not work:
mash.deep_fetch(:fields, 0).deep_locate(-> (key, value, object) { value.include?("product_group") }) { "ERROR: category" }
Example output:
% rake get_products
{:category=>nil, :name=>"Luxurous Jumpsuit", :image=>"http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/productLarge/129579-0012.jpg", :price=>"599", :description=>"Lorem ipsum dolor"}
Example mash:
#<Hashie::Mash brand="Dr Denim" categories=[#<Hashie::Mash name="Kvinne > KLÆR > Jeans > Slim">] description="Lorem ipsum dolor." fields=[#<Hashie::Mash name="sale" value="false">, #<Hashie::Mash name="product_id_original" value="226693-7698">, #<Hashie::Mash name="gender" value="Kvinne">, #<Hashie::Mash name="artNumber" value="226693-7698">, #<Hashie::Mash name="productGroup" value="Jeans">, #<Hashie::Mash name="productStyle" value="Slim">, #<Hashie::Mash name="extraImageProductSmall" value="http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/cart_thumb/226693-7698.jpg">, #<Hashie::Mash name="productClass" value="Klær">, #<Hashie::Mash name="extraImageProductLarge" value="http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/productLarge/226693-7698.jpg">, #<Hashie::Mash name="sizes" value="W24/L32,W25/L32,W26/L32,W27/L32,W28/L32,W29/L32,W30/L32,W31/L32,W32/L32,W26/L30,W27/L30,W28/L30,W29/L30,W24/L30,W25/L30,W32/L30,W31/L30,W30/L30">, #<Hashie::Mash name="color" value="Mid Blue">] identifiers=#<Hashie::Mash sku="226693-7698"> language="no" name="Regina Jeans" offers=[#<Hashie::Mash feed_id=10086 id="2820760a-c5b2-494a-b5dd-ab713f796cb9" in_stock=1 modified=1474947357838 price_history=[#<Hashie::Mash date=1474949513421 price=#<Hashie::Mash currency="NOK" value="599">>] product_url="http://pdt.tradedoubler.com/click?a1234" program_logo="http://hst.tradedoubler.com/file/17833/2014-logos/200X200.png" program_name="Nelly NO" source_product_id="226693-7698">] product_image=#<Hashie::Mash url="http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/productLarge/226693-7698.jpg">>
get_products.rake:
# encoding: utf-8
# Gets messy JSON from other store via REST client and cleans it up with Hashie
require "rest_client"
require "hashie"
Product = Struct.new(:category, :name, :image, :price, :description)
module ProductsFromOtherStore
CATEGORIES = [
"festkjoler",
"jakker",
"jeans",
"jumpsuit",
"vesker"
]
def self.fetch
CATEGORIES.map do |category|
Tradedoubler.fetch category
end
end
# Prettify, ie. `fooBar` => `foo_bar`
def self.prettify(x)
case x
when Hash
x.map { |key, value| [key.underscore, prettify(value)] }.to_h
when Array
x.map { |value| prettify(value) }
else
x
end
end
end
class ProductsFromOtherStore::Tradedoubler
KEY = "FE34B1309AB749F1578AEE87D9D74535513F6B54"
# Products to fetch from API
LIMIT = 2
def self.fetch category
new(category).filtered_products.take(LIMIT)
rescue RestClient::RequestTimeout => e
Array.new
end
def initialize category
#category = category
# API doesn't support gender or category searches, so do some filtering based on available JSON fields
#filters = Array.new
define_filter { |mash|
mash.fields.any? { |field|
field.name == "gender" && field.value.downcase == "kvinne"
}
}
define_filter { |mash|
mash.categories.any? { |category|
category.name.underscore.include? #category
}
}
end
def define_filter(&filter)
#filters << filter
end
def filtered_products
filtered_mashes.map { |mash|
# puts mash
Product.new(
# mash.deep_fetch(:fields, 0).find { |field| field[:name] == "product_group" }[:value],
mash.deep_fetch(:fields, 0).deep_locate(-> (key, value, object) { value.include?("product_group") }) { "ERROR: category" },
mash.deep_fetch(:name) { "ERROR: name" },
mash.deep_fetch(:product_image, :url) { "ERROR: image URL" },
mash.deep_fetch(:offers, 0, :price_history, 0, :price, :value) { "ERROR: price" },
mash.deep_fetch(:description) { "ERROR: description" }
)
}
end
private
def request
response = RestClient::Request.execute(
:method => :get,
:url => "http://api.tradedoubler.com/1.0/products.json;q=#{ URI.encode(#category) };limit=#{ LIMIT }?token=#{ KEY }",
:timeout => 0.4
)
end
def hashes
ProductsFromOtherStore.prettify(JSON.parse(request)["products"])
end
def mashes
hashes.map { |hash| Hashie::Mash.new(hash) }.each do |mash|
mash.extend Hashie::Extensions::DeepFetch
mash.extend Hashie::Extensions::DeepLocate
end
end
def filtered_mashes
mashes.select { |mash| mash_matches_filter? mash }
end
def mash_matches_filter? mash
# `.all?` requires all filters to match, `.any?` requires only one
#filters.all? { |filter| filter.call mash }
end
end
# All that for this
task :get_products => :environment do
#all_products_from_all_categories = ProductsFromOtherStore.fetch
#all_products_from_all_categories.each do |products|
products.each do |product|
puts product.to_h
end
end
end
The messy JSON we got via rest-client:
{
"productHeader": {
"totalHits": 367
},
"products": [{
"name": "501 CT Jeans For Women",
"productImage": {
"url": "http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/productLarge/441576-1056.jpg"
},
"language": "no",
"description": "Jeans fra Levi's. Noe kortere nederst, fem lommer. Normal høyde på midjen, med hemper i linningen og knappegylfen. Dekorative slitte partier foran og nederst på benet.<br />Laget av 100% bomull.",
"brand": "Levis",
"identifiers": {
"sku": "441576-1056"
},
"fields": [{
"name": "sale",
"value": "false"
}, {
"name": "sizes",
"value": "W24/L32,W25/L32,W26/L32,W27/L32,W28/L32,W29/L32,W30/L32,W31/L32,W25/L34,W26/L34,W27/L34,W28/L34,W29/L34,W30/L34"
}, {
"name": "productStyle",
"value": "Straight"
}, {
"name": "gender",
"value": "Kvinne"
}, {
"name": "product_id_original",
"value": "441576-1056"
}, {
"name": "productGroup",
"value": "Jeans"
}, {
"name": "extraImageProductLarge",
"value": "http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/productLarge/441576-1056.jpg"
}, {
"name": "extraImageProductSmall",
"value": "http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/cart_thumb/441576-1056.jpg"
}, {
"name": "artNumber",
"value": "441576-1056"
}, {
"name": "productClass",
"value": "Klær"
}, {
"name": "color",
"value": "Indigo"
}],
"offers": [{
"feedId": 10086,
"productUrl": "http://pdt.tradedoubler.com/click?a(2402331)p(80279)product(57d37b9ce4b085c06c38c96b)ttid(3)url(http%3A%2F%2Fnelly.com%2Fno%2Fkl%C3%A6r-til-kvinner%2Fkl%C3%A6r%2Fjeans%2Flevis-441%2F501-ct-jeans-for-women-441576-1056%2F)",
"priceHistory": [{
"price": {
"value": "1195",
"currency": "NOK"
},
"date": 1473477532181
}],
"modified": 1473477532181,
"inStock": 1,
"sourceProductId": "441576-1056",
"programLogo": "http://hst.tradedoubler.com/file/17833/2014-logos/200X200.png",
"programName": "Nelly NO",
"id": "57d37b9ce4b085c06c38c96b"
}],
"categories": [{
"name": "Kvinne > KLÆR > Jeans > Straight"
}]
}, {
"name": "501 CT Jeans For Women",
"productImage": {
"url": "http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/productLarge/441576-6581.jpg"
},
"language": "no",
"description": "Jeans fra Levi's. Noe kortere nederst, fem lommer. Normal høyde på midjen, med hemper i linningen og knappegylfen. Dekorative slitte partier foran og nederst på benet.<br />Laget av 100% bomull.",
"brand": "Levis",
"identifiers": {
"sku": "441576-6581"
},
"fields": [{
"name": "sale",
"value": "false"
}, {
"name": "artNumber",
"value": "441576-6581"
}, {
"name": "productStyle",
"value": "Straight"
}, {
"name": "gender",
"value": "Kvinne"
}, {
"name": "extraImageProductLarge",
"value": "http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/productLarge/441576-6581.jpg"
}, {
"name": "extraImageProductSmall",
"value": "http://nlyscandinavia.scene7.com/is/image/nlyscandinavia/cart_thumb/441576-6581.jpg"
}, {
"name": "productGroup",
"value": "Jeans"
}, {
"name": "product_id_original",
"value": "441576-6581"
}, {
"name": "productClass",
"value": "Klær"
}, {
"name": "color",
"value": "Desert"
}, {
"name": "sizes",
"value": "W24/L32,W25/L32,W26/L32,W27/L32,W28/L32,W29/L32,W30/L32,W31/L32,W25/L34,W26/L34,W27/L34,W28/L34,W29/L34,W30/L34,W31/L34"
}],
"offers": [{
"feedId": 10086,
"productUrl": "http://pdt.tradedoubler.com/click?a(2402331)p(80279)product(57b3cafbe4b06cf59bc254bf)ttid(3)url(http%3A%2F%2Fnelly.com%2Fno%2Fkl%C3%A6r-til-kvinner%2Fkl%C3%A6r%2Fjeans%2Flevis-441%2F501-ct-jeans-for-women-441576-6581%2F)",
"priceHistory": [{
"price": {
"value": "1195",
"currency": "NOK"
},
"date": 1471400699283
}],
"modified": 1471400699283,
"inStock": 1,
"sourceProductId": "441576-6581",
"programLogo": "http://hst.tradedoubler.com/file/17833/2014-logos/200X200.png",
"programName": "Nelly NO",
"id": "57b3cafbe4b06cf59bc254bf"
}],
"categories": [{
"name": "Kvinne > KLÆR > Jeans > Straight"
}]
}]
}
There is a lot of things going on in your code sample. I tried to split in parts and restructure it. It does not do the same as your code but I think it should get you started and perhaps you can come back when you have a more specific question.
Note that I did not use hashie, I think that accessing some deeply nested hash structures in a few places does not justify adding a new library to a project.
Questions/Ideas/Hints:
are prices Integers or Floats?
Is the JSON consistent (all elements present all the time?)
Are you using Ruby 2.3? Then look into Hash#dig
Why did you prettify the JSON keys? Does not make sense to me as you build Product objects to work with anyway?
Unless there are performance issues i would convert all products to Ruby objects first and filter then. Just easier and more readable.
Code
Product (same as yours)
Product = Struct.new(:category, :name, :image, :price, :description)
JsonProductBuilder converts the parsed JSON to Product Objects.
class JsonProductBuilder
def initialize(json)
#json = json
end
def call
json.fetch('products', []).map do |item|
Product.new(
extract_category(item),
item['name'],
item.fetch('productImage', {})['url'],
extract_price(item),
item['description']
)
end
end
private
attr_reader :json
def extract_category(item)
field = item['fields'].find do |field|
field['name'] == 'productGroup'
end
field['value'] if field
end
def extract_price(item)
offer = item['offers'].first
history = offer['priceHistory'].first
value = history['price']['value']
Integer(value) # Or use Float?
end
end
CategoryFilter returns a limited subset of the products. You can easily add other filters and combine them. Perhaps you might want to look into lazy for performance improvements.
class CategoryFilter
def initialize(products, *categories)
#products = products
#categories = categories
end
def call
products.select do |product|
categories.include?(product.category)
end
end
private
attr_reader :products, :categories
end
Use it like this:
limit = 10
categories = ['laptop', 'something']
params = {
q: categories.join(','),
limit: limit,
}
paramsString = params.map do |key, value|
"#{key}=#{value}"
end.join(';')
response = RestClient.get(
"http://api.tradedoubler.com/1.0/products.json;#{paramsString}?token=#{token}"
)
json = JSON.parse(response)
products = JsonProductBuilder.new(json).call
puts products.size
products = CategoryFilter.new(products, 'Klær', 'Sko', 'Jeans').call
puts products.size
products.each do |product|
puts product.to_h
end

Algorithm to transform tree data in Ruby

How can i change my tree made of Array of hashes into another structure such as:
My data looks like :
{
"A": [
{ "A1": [] },
{ "A2": [] },
{
"A3": [
{
"A31": [
{ "A311": [] },
{ "A312": [] }
]
}
]
}
]
}
into something like :
{
"name": "A",
"children": [
{ "name": "A1" },
{ "name": "A2" },
{
"name": "A3",
"children": [
{
"name": "A31",
"children": [
{ "name": "A311" },
{ "name": "A312" }
]
}
]
}
]
}
I tried a few things but nothing worked as I hoped.
This is how i move into my tree
def recursive(data)
return if data.is_a?(String)
data.each do |d|
keys = d.keys
keys.each do |k|
recursive(d[k])
end
end
return data
end
I tried my best to follow how to ask so to clarify :
The tree can have a unlimited deeph
Names are more complexe than A1, A2 ...
λ = ->(h) { [h[:name], h[:children] ? h[:children].map(&λ).to_h : []] }
[λ.(inp)].to_h
#⇒ {
# "A" => {
# "A1" => [],
# "A2" => [],
# "A3" => {
# "A31" => {
# "A311" => [],
# "A312" => []
# }
# }
# }
# }
This solution returns hashes that are not wrapped in arrays inside. If you really want to wrap nested hashes with arrays, map them in λ.
When you don't know how to implement something, always think the simplest case first.
Step 1: Convert {"A1" => []} to{"name" => "A1", "children" => []}
This is simple
def convert(hash)
pair = hash.each_pair.first
["name", "children"].zip(pair).to_h
end
Step2: Recursively convert all hashes in children
def convert(hash)
pair = hash.each_pair.first
pair[1] = pair[1].map{|child| convert(child)}
["name", "children"].zip(pair).to_h
end
Step 3: Handle corner cases
If children is empty then omit it.
def convert(hash)
pair = hash.each_pair.first
pair[1] = pair[1].map{|child| convert(child)}
result = {"name" => pair[0]}
result.merge!("children" => pair[1]) if pair[1].any?
result
end

How find a nth node value using ruby

I have a JSON response tree like structure
{
"id":""
"node": [
{
"id":""
"node": [
{
"id":""
"node":[]
}
]
}
]
}
How could I get the last id value, it's just example it may contain n number of loops.
h = {
"id" => "1",
"node" => [
{
"id" => "2",
"node" => [
{
"id" => "3",
"node" => []
}
]
}
]
}
▶ λ = ->(h) { h['node'].empty? ? h['id'] : λ.(h['node'].last) }
#⇒ #<Proc:0x00000002f4b490#(pry):130 (lambda)>
▶ λ.(h)
#⇒ "3"
Maybe this method will helps you. You can call recursion method with sub hash.
h = {
"id" => "1",
"node" => [
{
"id" => "2",
"node" => [
{
"id" => "3",
"node" => []
}
]
}
]
}
def get_last_node(h)
if Array === h['node'] && !h['node'].empty?
h['node'].each do |node_h|
id = send(__callee__, node_h)
return id if id
end
nil
else
h['id']
end
end
get_last_node(h)
# => 3
Similar to #mudasobwa's answer:
def get_last_node(h)
h["node"].empty? ? h["id"] : get_last_node(h["node"].first)
end
get_last_node(h)
#=> 3

Convert csv to json in ruby

CSV
id,modifier1_name,modifier2_price,modifier2_name,modifier2_price,modifier2_status
1,'Small',10,'Large',20,'YYY'
2,'Small',20,'Large',30,'YYY'
JSON
[
{
id: 1,
modifier: [
{name: 'Small', price: 10},
{name: 'Large', price: 20, status: 'YYY'}]
},
{
id: 2,
modifier: [
{name: 'Small', price: 20},
{name: 'Large', price: 30, status: 'YYY'}],
}
]
How to convert CSV to Json in this case when modifiers can be different ?
You will need to map the modifiers yourself, as there is no built-in method to map hash values into an array from your logic:
JSON.pretty_generate(CSV.open('filename.csv', headers: true).map do |row|
modifier = {}
row.each do |k, v|
if k =~ /modifier(.)_(.*)$/
(modifier[$1] ||= {})[$2] = v
end
end
{ id: row['id'],
modifier: modifier.sort_by { |k, v| k }.map {|k, v| v }
}
end)
For the file*
id,modifier1_name,modifier1_price,modifier2_name,modifier2_price,modifier2_status
1,Small,10,Large,20,YYY
2,Small,20,Large,30,YYY
*I made some changes to the file you show, since it will not give you the required result - you state modifier2_price twice, for example
You will get:
[
{
"id": "1",
"modifier": [
{
"name": "Small",
"price": "10"
},
{
"name": "Large",
"price": "20",
"status": "YYY"
}
]
},
{
"id": "2",
"modifier": [
{
"name": "Small",
"price": "20"
},
{
"name": "Large",
"price": "30",
"status": "YYY"
}
]
}
]
require 'csv'
require 'json'
CSV.open('filename.csv', :headers => true).map { |x| x.to_h }.to_json

Resources