Parse Hash in Ruby - ruby-on-rails

How can I parse a hash in ROR?
I have a hash in string format(enclosed by double quotes) and i need to parse them to a valid hash.
eg.
input_hash = "{"name" => "john"}"
desired
output_hash = {"name" => "john"}

This is the wrong approach. String representation of a ruby hash is not a good way to serialise data. It is well structured, and definitely possible to get it back to a ruby hash (eval), but it's extremely dangerous and can give an attacker who has control over the input string full control over your system.
Approach the problem from a different angle. Look for where the string gets stored and change the code there instead. Store it for example as JSON. Then it can easily and safely be parsed back to a hash and can also be sent to systems running on something that is not ruby.

Related

Ruby on Rails: How to pluck hash from a string

I have a record of class String something like:
"{'1'=>'abc', '2'=> 'def'}"
but from this i need this in class Hash, something like:
{'1'=>'abc', '2'=> 'def'}
how to convert like this?
Edit: in my case i was getting this from CSV from other service where they were converting Hash into String before sending, so i asked them to send it using Base64.encode64 and now i am able to decode and get it in Hash format.
If you trust that the data is not nefarious you can write
str = "{'1'=>'abc', '2'=> 'def'}"
h = eval str
#=> {"1"=>"abc", "2"=>"def"}
See Kernel#eval.
If the data could be nefarious it would be safer to write
str.gsub(/{ *| *\|'/, '')
.split(/ *, */)
.map { |s| s.split(/ *=> */) }
.to_h
#=> {"1"=>"abc", "2"=>"def"}
This answer might not related to the question that i asked, but this is what solved my case, since other service was sending few Hash records in CSV format, they were converting the Hash to String before posting it to me.
So i asked them to use Base64.encode64 and send the encoded string in CSV.
Base64.encode64({'1'=>'abc', '2'=> 'def'}.to_json).delete("\n")
#=> "eyIxIjoiYWJjIiwiMiI6ImRlZiJ9"
and when i get the string i am converting it back to Hash something like
JSON.parse(Base64.decode64("eyIxIjoiYWJjIiwiMiI6ImRlZiJ9"))
#=> {"1"=>"abc", "2"=>"def"}
If I were writing a parser for objects that were in Ruby's object notation, not Javascript's, I'd call it either RON or RSON. I like the pun in the second better, so I think I'd go with RSON.
A quick google confirms that at least one other person thinks like I do, or at least arrives at the same conclusion: https://github.com/tannevaled/rson. So one option would be to use the rson gem, if it still works.
Alternatively, if you're not expecting any quotes or => to appear in any of the strings, you'd probably get away with something like
h = JSON.parse(str.gsub(/\s*=>\s*/, ":").gsub("'", '"'))

Unable to convert url params string to hash and then back to url string

Input is a params string (Input format cannot be changed)
something=1,2,3,4,5&something_else=6,7,8
Expected Output:
something=1,2,3,4,5&something_else=6,7,8
What I am doing:
params = 'something=1,2,3,4,5'
CGI::parse(params)
CGI.unescape(CGI.parse(params).to_query)
And I am getting this as output:
something[]=1,2,3,4,5
When I did CGI::parse(params)
I am getting this : {"something"=>["1,2,3,4,5"]}
which is wrong because it is not an array, something is a string which is "1,2,3,4,5" but it is being converted as array when I did CGI parse.
The reason I need to do CGI parse is because I need to manipulate the url PARAMS.
Is there any other possible way where I can convert it in the right way and maintain the params format?
The CGI module is a complete dinosaur and should probably be thrown in the garbage because of how bad it is, but for some reason it persists in the Ruby core. Maybe some day someone will refactor it and make it workable. Until then, skip it and use something better like URI, which is also built-in.
Given your irregular, non-compliant query string:
query_string = 'something=1,2,3,4,5&something_else=6,7,8'
You can handle this by using the decode_www_form method which handles query-strings:
require 'uri'
decoded = URI.decode_www_form(query_string).to_h
# => {"something"=>"1,2,3,4,5", "something_else"=>"6,7,8"}
To re-encode it you just call encode_www_form and then force unescape to undo what it's correctly doing to handle the , values:
encoded = URI.unescape(URI.encode_www_form(decoded))
# => "something=1,2,3,4,5&something_else=6,7,8"
That should get the effect you want.

Array is stored as string. How do I convert it back to any array?

I am using Rails 4.2 with Redis. When I store and retrieve an array with Redis, it returns a formatted string. How do I return this string to an array?
The returned string is exactly this, though the number and value of entries will obviously vary:
"[\"FCF1115A\", \"FCF1116A\"]"
Obviously, it could be parsed, but is there some function that would handle this? I could probably format a better string myself, rather than allowing Redis to do so. Thanks...
If using JSON library is an option. Following is tested in irb:
> require 'json'
> puts JSON.parse("[\"FCF1115A\", \"FCF1116A\"]").to_json
=> ["FCF1115A","FCF1116A"]

Parse a string as if it were a querystring in Ruby on Rails

I have a string like this:
"foo=bar&bar=foo&hello=hi"
Does Ruby on Rails provide methods to parse this as if it is a querystring, so I get a hash like this:
{
:foo => "bar",
:bar => "foo",
:hello => "hi"
}
Or must I write it myself?
EDIT
Please note that the string above is not a real querystring from a URL, but rather a string stored in a cookie from Facebook Connect.
The answer depends on the version of Rails that you are using. If you are using 2.3 or later, use Rack's builtin parser for params
Rack::Utils.parse_nested_query("a=2") #=> {"a" => "2"}
If you are on older Rails, you can indeed use CGI::parse. Note that handling of hashes and arrays differs in subtle ways between modules so you need to verify whether the data you are getting is correct for the method you choose.
You can also include Rack::Utils into your class for shorthand access.
The
CGI::parse("foo=bar&bar=foo&hello=hi")
Gives you
{"foo"=>["bar"], "hello"=>["hi"], "bar"=>["foo"]}
Edit:
As specified by Ryan Long this version accounts for multiple values of the same key, which is useful if you want to parse arrays too.
Edit 2:
As Ben points out, this may not handle arrays well when they are formatted with ruby on rails style array notation.
The rails style array notation is: foo[]=bar&foo[]=nop. That style is indeed handled correctly with Julik's response.
This version will only parse arrays correctly, if you have the params like foo=bar&foo=nop.
Edit : as said in the comments, symolizing keys can bring your server down if someone want to hurt you. I still do it a lot when I work on low profile apps because it makes things easier to work with but I wouldn't do it anymore for high stake apps
Do not forget to symbolize the keys for obtaining the result you want
Rack::Utils.parse_nested_query("a=2&b=tralalala").deep_symbolize_keys
this operation is destructive for duplicates.
If you talking about the Urls that is being used to get data about the parameters them
> request.url
=> "http://localhost:3000/restaurants/lokesh-dhaba?data=some&more=thisIsMore"
Then to get the query parameters. use
> request.query_parameters
=> {"data"=>"some", "more"=>"thisIsMore"}
If you want a hash you can use
Hash[CGI::parse(x).map{|k,v| [k, v.first]}]

how to parse multivalued field from URL query in Rails

I have a URL of form http://www.example.com?foo=one&foo=two
I want to get an array of values ['one', 'two'] for foo, but params[:foo] only returns the first value.
I know that if I used foo[] instead of foo in the URL, then params[:foo] would give me the desired array.
However, I want to avoid changing the structure of the URL if possible, since its form is provided as a spec to a client application. is there a good way to get all the values without changing the parameter name?
You can use the default Ruby CGI module to parse the query string in a Rails controller like so:
params = CGI.parse(request.query_string)
This will give you what you want, but note that you won't get any of Rails other extensions to query string parsing, such as using HashWithIndifferentAccess, so you will have to us String rather than Symbol keys.
Also, I don't believe you can set params like that with a single line and overwrite the default rails params contents. Depending on how widespread you want this change, you may need to monkey patch or hack the internals a little bit. However the expeditious thing if you wanted a global change would be to put this in a before filter in application.rb and use a new instance var like #raw_params
I like the CGI.parse(request.query_string) solution mentioned in another answer. You could do this to merge the custom parsed query string into params:
params.merge!(CGI.parse(request.query_string).symbolize_keys)

Resources