It looks like you tried to create your own JSON serializer, instead of using the one that comes with Ruby. Or, perhaps you didn’t realize that JSON was a thing and wanted to store arrays of hashes and tried to invent your own. Either way, it wasn’t a good path to follow.
This is your definition, which, after being retrieved would remain a String. It appears to be the elements of an Array of Hashes, without the surrounding Array [
and ]
:
foo = "{\"monday\"=>\"{:start_time=>9.0, :end_time=>10.5}\"},{\"tuesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"},{\"wednesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"}"
and it remains a String:
foo.class # => String
It’d be possible to munge it back into a form that could be evaluated and turned back into an Array of Hashes, but it’s not straightforward, nor is it clean or convenient or elegant.
I suspect your original object looked like this:
foo = {"monday"=>{:start_time=>9.0, :end_time=>10.5}},{"tuesday"=>{:start_time=>8.5, :end_time=>10.0}},{"wednesday"=>{:start_time=>8.5, :end_time=>10.0}}
# => [{"monday"=>{:start_time=>9.0, :end_time=>10.5}},
# {"tuesday"=>{:start_time=>8.5, :end_time=>10.0}},
# {"wednesday"=>{:start_time=>8.5, :end_time=>10.0}}]
which is an Array:
foo.class # => Array
It’s an Array now because you have multiple Hash elements separated by commas (,
) which Ruby interprets as an Array when assigned to a single variable. This would be a less visually confusing example:
foo = {a: 1}, {a: 2} # => [{:a=>1}, {:a=>2}]
foo.class # => Array
foo.first.class # => Hash
Back to the Array of Hashes in foo
: Starting from that it’s easy to let the JSON class serialize it into a string that can be stored in a database, then retrieved and reparsed back into a Ruby object:
require 'json'
foo.to_json
foo.to_json
would result in a serialized string that looks like:
[{"monday":{"start_time":9.0,"end_time":10.5}},{"tuesday":{"start_time":8.5,"end_time":10.0}},{"wednesday":{"start_time":8.5,"end_time":10.0}}]
And, given that string, JSON can rebuild the object:
bar = JSON.parse(foo.to_json)
# => [{"monday"=>{"start_time"=>9.0, "end_time"=>10.5}},
# {"tuesday"=>{"start_time"=>8.5, "end_time"=>10.0}},
# {"wednesday"=>{"start_time"=>8.5, "end_time"=>10.0}}]
Note that it’s not necessary to use parse
or to_json
. The class is smart enough to recognize whether the parameter is a String, or an Array or Hash, and parse or serialize respectively:
JSON[JSON[foo]]
# => [{"monday"=>{"start_time"=>9.0, "end_time"=>10.5}},
# {"tuesday"=>{"start_time"=>8.5, "end_time"=>10.0}},
# {"wednesday"=>{"start_time"=>8.5, "end_time"=>10.0}}]
At this point you’re prepared to store the values into a String-type field in your database, then retrieve them later and reuse them.
If you are hoping to search or manipulate those stored JSON strings, I’d recommend re-thinking that. While a modern DBM can search inside JSON and generate it, you’d be better off having a separate table of the keys and values. It’s faster and more flexible.
Finally to your question, how to convert the string so you can get the keys and values:
foo = "{\"monday\"=>\"{:start_time=>9.0, :end_time=>10.5}\"},{\"tuesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"},{\"wednesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"}"
bar = eval('[' + foo.gsub(/"([{}])/, '\1') + ']')
bar
# => [{"monday"=>{:start_time=>9.0, :end_time=>10.5}},
# {"tuesday"=>{:start_time=>8.5, :end_time=>10.0}},
# {"wednesday"=>{:start_time=>8.5, :end_time=>10.0}}]
bar.map { |h| [h.keys, h.values.first.keys] }
# => [[["monday"], [:start_time, :end_time]],
# [["tuesday"], [:start_time, :end_time]],
# [["wednesday"], [:start_time, :end_time]]]
This hints that the Array of Hashes wasn’t the right structure either. A simple hash would have sufficed:
foo = {
"monday"=>{:start_time=>9.0, :end_time=>10.5},
"tuesday"=>{:start_time=>8.5, :end_time=>10.0},
"wednesday"=>{:start_time=>8.5, :end_time=>10.0}
}
require 'json'
bar = JSON[JSON[foo]] # imitate a round-trip to/from the DB
bar.keys # => ["monday", "tuesday", "wednesday"]
bar.values # => [{"start_time"=>9.0, "end_time"=>10.5}, {"start_time"=>8.5, "end_time"=>10.0}, {"start_time"=>8.5, "end_time"=>10.0}]
1
solved Regular expression to split ruby strings into multiple hashes