from json to a ruby hash? - ruby

I can go one way using
require 'json'
def saveUserLib(user_lib)
File.open("/Users/name/Documents/user_lib.json","w") do |f|
f.write($user_lib.to_json)
end
end
uname = gets.chomp
$user_lib["_uname"] = uname
saveUserLib($user_lib)
but how do i get it back again as my user_lib?

You want JSON.parse or JSON.load:
def load_user_lib( filename )
JSON.parse( IO.read(filename) )
end
The key here is to use IO.read as a simple way to load the JSON string from disk, so that it can be parsed. Or, if you have UTF-8 data in your file:
my_object = JSON.parse( IO.read(filename, encoding:'utf-8') )
I've linked to the JSON documentation above, so you should go read that for more details. But in summary:
json = my_object.to_json — method on the specific object to create a JSON string.
json = JSON.generate(my_object) — create JSON string from object.
JSON.dump(my_object, someIO) — create a JSON string and write to a file.
my_object = JSON.parse(json) — create a Ruby object from a JSON string.
my_object = JSON.load(someIO) — create a Ruby object from a file.
Alternatively:
def load_user_lib( filename )
File.open( filename, "r" ) do |f|
JSON.load( f )
end
end
Note: I have used a "snake_case" name for the method corresponding to your "camelCase" saveUserLib as this is the Ruby convention.

JSON.load will do the trick. Here's an example that goes both ways:
>> require 'json'
=> true
>> a = {"1" => "2"}
=> {"1"=>"2"}
>> b = JSON.dump(a)
=> "{\"1\":\"2\"}"
>> c = JSON.load(b)
=> {"1"=>"2"}

here is some example:
require 'json'
source_hash = {s: 12, f: 43}
json_string = JSON.generate source_hash
back_to_hash = JSON.parse json_string

Related

Ruby Convert String to Hash

I'm storing configuration data in hashes written in flat files. I want to import the hashes into my Class so that I can invoke corresponding methods.
example.rb
{
:test1 => { :url => 'http://www.google.com' },
:test2 => {
{ :title => 'This' } => {:failure => 'sendemal'}
}
}
simpleclass.rb
class Simple
def initialize(file_name)
# Parse the hash
file = File.open(file_name, "r")
#data = file.read
file.close
end
def print
#data
end
a = Simple.new("simpleexample.rb")
b = a.print
puts b.class # => String
How do I convert any "Hashified" String into an actual Hash?
You can use eval(#data), but really it would be better to use a safer and simpler data format like JSON.
You can try YAML.load method
Example:
YAML.load("{test: 't_value'}")
This will return following hash.
{"test"=>"t_value"}
You can also use eval method
Example:
eval("{test: 't_value'}")
This will also return same hash
{"test"=>"t_value"}
Hope this will help.
I would to this using the json gem.
In your Gemfile you use
gem 'json'
and then run bundle install.
In your program you require the gem.
require 'json'
And then you may create your "Hashfield" string by doing:
hash_as_string = hash_object.to_json
and write this to your flat file.
Finally, you may read it easily by doing:
my_hash = JSON.load(File.read('your_flat_file_name'))
This is simple and very easy to do.
Should it not be clear, it is only the hash that must be contained in a JSON file. Suppose that file is "simpleexample.json":
puts File.read("simpleexample.json")
# #{"test1":{"url":"http://www.google.com"},"test2":{"{:title=>\"This\"}":{"failure":"sendemal"}}}
The code can be in a normal Ruby source file, "simpleclass.rb":
puts File.read("simpleclass.rb")
# class Simple
# def initialize(example_file_name)
# #data = JSON.parse(File.read(example_file_name))
# end
# def print
# #data
# end
# end
Then we can write:
require 'json'
require_relative "simpleclass"
a = Simple.new("simpleexample.json")
#=> #<Simple:0x007ffd2189bab8 #data={"test1"=>{"url"=>"http://www.google.com"},
# "test2"=>{"{:title=>\"This\"}"=>{"failure"=>"sendemal"}}}>
a.print
#=> {"test1"=>{"url"=>"http://www.google.com"},
# "test2"=>{"{:title=>\"This\"}"=>{"failure"=>"sendemal"}}}
a.class
#=> Simple
To construct the JSON file from the hash:
h = { :test1=>{ :url=>'http://www.google.com' },
:test2=>{ { :title=>'This' }=>{:failure=>'sendemal' } } }
we write:
File.write("simpleexample.json", JSON.generate(h))
#=> 95

Gsub in-place not working

I have this code:
Firm.all.each do |firm|
url = firm.site
doc = Nokogiri::HTML(open(url))
data = doc.css("##{firm.menu_id} a")
data.each do |e|
e.text.strip!
e.text.gsub!(/[\n\t]*/,'')
puts e.text
end
end
The strings are being displayed in the same format as the input (that means, the gsub! method is not affecting the string). I think that e.text can be immutable, but I'd like to ensure that.
The text method returns a new String each time, which can be seen using object_id:
e = Nokogiri::XML('<a>text</a>')
e.text.object_id == e.text.object_id # => false
If you want to modify the node's text, set the content:
e.at_css('a').content = "foo"
e.text # => "foo"

parsing a json string with duplicate keys in ruby

i have a json with duplicate keys as below:
{"a":{
"stateId":"b",
"countyId":["c"]
},"a":{
"stateId":"d",
"countyId":["e"]
}}
When i use JSON.parse or JSON(stirng), it parses and gives me the key with values d, e. I need to parse the json such that it it avoids parsing the same key twice and has b, c values for the key 'a' instead of 'd', 'e'.
There is a way. Instead of using the ususal Hash class for parsing JSON objects, use a slightly modified class, which can check if a key already exists:
class DuplicateCheckingHash < Hash
attr_accessor :duplicate_check_off
def []=(key, value)
if !duplicate_check_off && has_key?(key) then
fail "Failed: Found duplicate key \"#{key}\" while parsing json! Please cleanup your JSON input!"
end
super
end
end
json = '{"a": 1, "a": 2}' # duplicate!
hash = JSON.parse(json, { object_class:DuplicateCheckingHash }) # will raise
json = '{"a": 1, "b": 2}'
hash = JSON.parse(json, { object_class:DuplicateCheckingHash })
hash.duplicate_check_off = true # make updateable again
hash["a"] = 42 # won't raise
It all depends on the format of your string. If it's as simple as you posted:
require 'json'
my_json =<<END_OF_JSON
{"a":{
"stateId":"b",
"countyId":["c"]
},"a":{
"stateId":"d",
"countyId":["e"]
},"b":{
"stateId":"x",
"countyId":["y"]
},"b":{
"stateId":"no",
"countyId":["no"]
}}
END_OF_JSON
results = {}
hash_strings = my_json.split("},")
hash_strings.each do |hash_str|
hash_str.strip!
hash_str = "{" + hash_str if not hash_str.start_with? "{"
hash_str += "}}" if not hash_str.end_with? "}}"
hash = JSON.parse(hash_str)
hash.each do |key, val|
results[key] = val if not results.keys.include? key
end
end
p results
--output:--
{"a"=>{"stateId"=>"b", "countyId"=>["c"]}, "b"=>{"stateId"=>"x", "countyId"=>["y"]}}

One-liner to Convert Nested Hashes into dot-separated Strings in Ruby?

What's the simplest method to convert YAML to dot-separated strings in Ruby?
So this:
root:
child_a: Hello
child_b:
nested_child_a: Nesting
nested_child_b: Nesting Again
child_c: K
To this:
{
"ROOT.CHILD_A" => "Hello",
"ROOT.CHILD_B.NESTED_CHILD_A" => "Nesting",
"ROOT.CHILD_B.NESTED_CHILD_B" => "Nesting Again",
"ROOT.CHILD_C" => "K"
}
It's not a one-liner, but perhaps it will fit your needs
def to_dotted_hash(source, target = {}, namespace = nil)
prefix = "#{namespace}." if namespace
case source
when Hash
source.each do |key, value|
to_dotted_hash(value, target, "#{prefix}#{key}")
end
when Array
source.each_with_index do |value, index|
to_dotted_hash(value, target, "#{prefix}#{index}")
end
else
target[namespace] = source
end
target
end
require 'pp'
require 'yaml'
data = YAML.load(DATA)
pp data
pp to_dotted_hash(data)
__END__
root:
child_a: Hello
child_b:
nested_child_a: Nesting
nested_child_b: Nesting Again
child_c: K
prints
{"root"=>
{"child_a"=>"Hello",
"child_b"=>{"nested_child_a"=>"Nesting", "nested_child_b"=>"Nesting Again"},
"child_c"=>"K"}}
{"root.child_c"=>"K",
"root.child_b.nested_child_a"=>"Nesting",
"root.child_b.nested_child_b"=>"Nesting Again",
"root.child_a"=>"Hello"}

What is the most elegant way in Ruby to remove a parameter from a URL?

I would like to take out a parameter from a URL by its name without knowing which parameter it is, and reassemble the URL again.
I guess it is not that hard to write something on my own using CGI or URI, but I imagine such functionality exists already. Any suggestions?
In:
http://example.com/path?param1=one&param2=2&param3=something3
Out:
http://example.com/path?param2=2&param3=something3
I prefer to use:
require 'addressable/uri'
uri = Addressable::URI.parse('http://example.com/path?param1=one&param2=2&param3=something3')
params = uri.query_values #=> {"param1"=>"one", "param2"=>"2", "param3"=>"something3"}
params.delete('param1') #=> "one"
uri.query_values = params #=> {"param2"=>"2", "param3"=>"something3"}
uri.to_s #=> "http://example.com/path?param2=2&param3=something3"
Maybe a little off-topic, but for anyone who's attempting to do this in the context of a rails app you can simply do:
url_for(params.except(:name_of_param_to_delete))
N.B. Tested in rails v2.3.9.
If you don't want to include an extra Gem and if you don't want nasty Regex, here's my prefered way:
require 'cgi'
require 'uri'
url = "http://example.com/path?param1=one&param2=2&param3=something3"
uri = URI(url) #=> #<URI::HTTP:0x007fbe25141a78 URL:http://example.com/path?param1=one&param2=2&param3=something3>
params = Rack::Utils.parse_nested_query(uri.query || "") #=> {"param1"=>["one"], "param2"=>["2"], "param3"=>["something3"]}
params.delete('param1') #=> ["one"]
uri.query = URI.encode_www_form(params) #=> "param2=2&param3=something3"
uri.to_s #=> "http://example.com/path?param2=2&param3=something3"
Note, choose wisely:
CGI.parse('a=b&a=c') #=> {"a"=>["b", "c"]}
Rack::Utils.parse_nested_query('a=b&a=c') #=> {"a"=>"c"}
And:
URI.encode_www_form({ a: :b, c: { d: 2 }}) #=> "a=b&c=%7B%3Ad%3D%3E2%7D"
{ a: :b, c: { d: 2 }}.to_query #=> "a=b&c%5Bd%5D=2"
The addressable gem will do this nicely; please see the superior answer by The Tin Man. But if you want to roll your own, here's how. The only claim this code has to elegance is that it hides the ugly in a method:
#!/usr/bin/ruby1.8
def reject_param(url, param_to_reject)
# Regex from RFC3986
url_regex = %r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$"
raise "Not a url: #{url}" unless url =~ url_regex
scheme_plus_punctuation = $1
authority_with_punctuation = $3
path = $5
query = $7
fragment = $9
query = query.split('&').reject do |param|
param_name = param.split(/[=;]/).first
param_name == param_to_reject
end.join('&')
[scheme_plus_punctuation, authority_with_punctuation, path, '?', query, fragment].join
end
url = "http://example.com/path?param1=one&param2=2&param3=something3"
p url
p reject_param(url, 'param2')
# => "http://example.com/path?param1=one&param2=2&param3=something3"
# => "http://example.com/path?param1=one&param3=something3"
I came up with something like this
def uri_remove_param(uri, params = nil)
return uri unless params
params = Array(params)
uri_parsed = URI.parse(uri)
return uri unless uri_parsed.query
escaped = uri_parsed.query.grep(/&/).size > 0
new_params = uri_parsed.query.gsub(/&/, '&').split('&').reject { |q| params.include?(q.split('=').first) }
uri = uri.split('?').first
amp = escaped ? '&' : '&'
"#{uri}?#{new_params.join(amp)}"
end
One line should be enough:
url.sub(/\?param_to_remove=[^&]*/, '?').sub(/\&param_to_remove=[^&]*/, '').sub(/\?$/,'')
Here's a clean one if your url is a string:
def remove_param(url, param_name)
uri = URI(url)
params = []
URI.decode_www_form(uri.query || '').each do |param|
next if param[0] == param_name
params << param
end
uri.query = URI.encode_www_form(params)
uri.to_s
end
note: you could use this to remove a param with a specific value by changing:
def remove_param(url, param_value)
and
next if param[1] == param_value

Resources