How do I pretty print a hash to a Rails view? - ruby-on-rails

I have something like:
{"a":"1","b":"2","c":"3","asefw":"dfsef"}
I want to print it out in a view. What's the best way to do that?
I tried parsing it as a JSON object and using JSON.stringify, but it seems to mess up indentation.
Any advice? I don't mind a JavaScript solution.

How about:
require 'json'
hash = JSON['{"a":"1","b":"2","c":"3","asefw":"dfsef"}']
puts JSON.pretty_generate(hash)
Which outputs:
{
"a": "1",
"b": "2",
"c": "3",
"asefw": "dfsef"
}
JSON.pretty_generate is more of a debugging tool than something I'd rely on when actually generating JSON to be sent to a browser. The "pretty" aspect also means "bloated" and "slower" because of the added whitespace, but it is good for diagnosing and comprehending what is in the structure so it might work well for your needs.
One thing to remember is that HTML, when rendered by a browser, has whitespace gobbled up, so whitespace runs disappear. To avoid that you have to wrap the JSON output in a <pre> block to preserve the whitespace and line-breaks. Something like this should work:
<pre>
{
"a": "1",
"b": "2",
"c": "3",
"asefw": "dfsef"
}
</pre>

irb(main)> puts queried_object.pretty_inspect
From PrettyPrint, so may need to require 'pp' first for this to work.
This also works great for e.g. Rails.logger output.

<%= raw JSON.pretty_generate(hash).gsub(" "," ") %>

If you (like I) find that the pretty_generate option built into Ruby's JSON library is not "pretty" enough, I recommend my own NeatJSON gem for your formatting.
To use it gem install neatjson and then use JSON.neat_generate instead of JSON.pretty_generate.
Like Ruby's pp it will keep objects and arrays on one line when they fit, but wrap to multiple as needed. For example:
{
"navigation.createroute.poi":[
{"text":"Lay in a course to the Hilton","params":{"poi":"Hilton"}},
{"text":"Take me to the airport","params":{"poi":"airport"}},
{"text":"Let's go to IHOP","params":{"poi":"IHOP"}},
{"text":"Show me how to get to The Med","params":{"poi":"The Med"}},
{"text":"Create a route to Arby's","params":{"poi":"Arby's"}},
{
"text":"Go to the Hilton by the Airport",
"params":{"poi":"Hilton","location":"Airport"}
},
{
"text":"Take me to the Fry's in Fresno",
"params":{"poi":"Fry's","location":"Fresno"}
}
],
"navigation.eta":[
{"text":"When will we get there?"},
{"text":"When will I arrive?"},
{"text":"What time will I get to the destination?"},
{"text":"What time will I reach the destination?"},
{"text":"What time will it be when I arrive?"}
]
}
It also supports a variety of formatting options to further customize your output. For example, how many spaces before/after colons? Before/after commas? Inside the brackets of arrays and objects? Do you want to sort the keys of your object? Do you want the colons to all be lined up?
For example, using your example Hash, you can get these different outputs, depending on what you want:
// JSON.neat_generate(o, wrap:true)
{
"a":"1",
"b":"2",
"c":"3",
"asefw":"dfsef"
}
// JSON.neat_generate o, wrap:true, aligned:true
{
"a" :"1",
"b" :"2",
"c" :"3",
"asefw":"dfsef"
}
// JSON.neat_generate o, wrap:true, aligned:true, around_colon:1
{
"a" : "1",
"b" : "2",
"c" : "3",
"asefw" : "dfsef"
}

You can try the gem awesome_print works very well, and in your view write
<%= ap(your_hash, plain: true, indent: 0).html_safe %>
also, you can change the values for config the styles to hash view

The given response is works fine, but if you want to have prettier and more custom pretty hash, use awesome_print
require 'awesome_print'
hash = JSON['{"a":"1","b":"2","c":"3","asefw":"dfsef"}']
ap hash
Cheers!

Pretty Print Hash using pure Ruby (no gems)
I came across this thread trying to solve this problem for myself.
I had a large Hash that I wanted to make pretty, but I needed to stay in ruby hash notation instead of JSON.
This is the code + examples
Use pretty_generate to get a nice formatted JSON string.
Replace all the JSON keys with symbol: equivalent
puts JSON.pretty_generate(result)
.gsub(/(?:\"|\')(?<key>[^"]*)(?:\"|\')(?=:)(?:\:)/) { |_|
"#{Regexp.last_match(:key)}:"
}
Sample JSON
{
"extensions": {
"heading": "extensions",
"take": "all",
"array_columns": [
"name"
]
},
"tables": {
"heading": "tables",
"take": "all",
"array_columns": [
"name"
]
},
"foreign_keys": {
"heading": "foreign_keys",
"take": "all",
"array_columns": [
"name"
]
},
"all_indexes": {
"heading": "all_indexes",
"take": "all",
"array_columns": [
"name"
]
},
"keys": {
"heading": "keys",
"take": "all",
"array_columns": [
"name"
]
}
}
Sample Ruby Hash
{
extensions: {
heading: "extensions",
take: "all",
array_columns: [
"name"
]
},
tables: {
heading: "tables",
take: "all",
array_columns: [
"name"
]
},
foreign_keys: {
heading: "foreign_keys",
take: "all",
array_columns: [
"name"
]
},
all_indexes: {
heading: "all_indexes",
take: "all",
array_columns: [
"name"
]
},
keys: {
heading: "keys",
take: "all",
array_columns: [
"name"
]
}
}

Related

Renaming type for FSharp.Data JsonProvider

I have a JSON that looks something like this:
{
...
"names": [
{
"value": "Name",
"language": "en"
}
],
"descriptions": [
{
"value": "Sample description",
"language" "en"
}
],
...
}
When using JsonProvider from the FSharp.Data library, it maps both fields as the same type MyJsonProvider.Name. This is a little confusing when working with the code. Is there any way how to rename the type to MyJsonProvider.NameOrDescription? I have read that this is possible for the CsvProvider, but typing
JsonProvider<"./Resources/sample.json", Schema="Name->NameOrDescription">
results in an error.
Also, is it possible to define that the Description field is actually an Option<MyJsonProvider.NameOrDescription>? Or do I just have to define the JSON twice, once with all possible values and the second time just with mandatory values?
[
{
...
"names": [
{
"value": "Name",
"language": "en"
}
],
"descriptions": [
{
"value": "Sample description",
"language" "en"
}
],
...
},
{
...
"names": [
{
"value": "Name",
"language": "en"
}
],
...
}
]
To answer your first question, I do not think there is a way of specifying such renaming. It would be quite reasonable option, but the JSON provider could also be more clever when generating names here (it knows that the type can represent Name or Description, so it could generate a name with Or based on those).
As a hack, you could add an unusued field with the right name:
type A = JsonProvider<"""{
"do not use": { "value_with_langauge": {"value":"A", "language":"A"} },
"names": [ {"value":"A", "language":"A"} ],
"descriptions": [ {"value":"A", "language":"A"} ]
}""">
To answer your second question - your names and descriptions fields are already arrays, i.e. ValueWithLanguge[]. For this, you do not need an optional value. If they are not present, the provider will simply give you an empty array.

Filter through nested JSON object and obtain JSON with specific keys, using Ruby

I currently have a nested JSON object which resembles
{
"People": [
{
"Name": "James",
"Age": "18",
"Gender": "Male",
"Sports": []
},
{
"Name": "Sarah",
"Age": "19",
"Gender": "Female",
"Sports": [
"Soccer",
"Basketball",
"Football"
]
}
]
}
Being new to Ruby, I aim to filter throught the entire json and return only the json object/objects in which the "Sports" array has content. So in the above scenario I expect to obtain the object below as a final outcome:
{
"Name": "Sarah",
"Age": "19",
"Gender": "Female",
"Sports": [
"Soccer",
"Basketball",
"Football"
]
}
Will I have to initiate a new method to perform such an act? Or would using regular ruby calls work in this case?
Although #philipyoo answer is right, it miss an explanation on how to "filter" the parsed JSON. If you are new to ruby, take a look at Array#keep_if : http://ruby-doc.org/core-2.2.0/Array.html#method-i-keep_if
require 'json'
people = JSON.parse("{long JSON data ... }")
people_with_sports = people.fetch('People', []).keep_if do |person|
!person.fetch('Sports', []).empty?
end
If you're getting a JSON object from a request, you want to parse it and then you can traverse the hash and arrays to find the information you need. See http://ruby-doc.org/stdlib-2.0.0/libdoc/json/rdoc/JSON.html
In your case, something like this:
require 'json'
parsed_json = JSON.parse('{"People": [ ... ]}')
parsed_json["People"].each do |person|
puts person if person["name"] == "Sarah"
end

Ruby/Rails - interate over complex (nested) JSON elements to create objects

I'm parsing some JSON from a mixed content source, and with it trying to store it with ActiveRecord.
At the moment I'm using a ton of variables:
json['settings']['newsletters']['weekly']
json['info']['address']['city']
Or trying to make things a little easier:
newsletters = json['settings']['newsletters']
newsletters['weekly']
address = json['info']['address']
address['city']
But this is all getting very messy, and not DRY.
I think the better way to do this would be to iterate over each element that is a hash (and therefore 'complex'), and assign it it's own object. This way, I don't have to declare a trillion variables, they can instead be assigned from the context of the JSON input.
So, I can do something like this:
user = json['input']
user.settings.newsletters.weekly
user.info.address.city
This is inspired by what ActiveResource documents:
# Any complex element (one that contains other elements) becomes its own object:
#
# {"id":1,"first":"Tyler","address":{"street":"Paper St.","state":"CA"}}
tyler = Person.find(1)
tyler.address # => <Person::Address::xxxxx>
tyler.address.street # => 'Paper St.'
Here is the JSON, reduced for brevity's sake:
{
"username": "robert_fitzsimmonds",
"emails": [{
"id_number": 1,
"address": "robert_fitzsimmonds#yahoo.com",
"confirmed": false
}, {
"id_number": 2,
"address": "robert_fitzsimmonds#gmail.com",
"confirmed": true
}],
"settings": {
"marketing": {
"main": true,
"weekly": false,
"daily": false
},
"language": "English"
},
"info": {
"address": {
"line_1": "31 Mole Road",
"line_2": "",
"city": "London",
"post_code": "NE4 5RJ"
},
"shared_account": false
}
}
Would such an iteration be the most efficient solution, or is it best to stick to long, messy variables?
Use the hash_dot gem if you can https://github.com/adsteel/hash_dot

Parse JSON with an array in Rails

I have the following JSON string returned by a remote server:
{
"users": [
{
"user_id": 1,
"name": "Chris Rivers",
},
{
"user_id": 3,
"name": "Peter Curley",
}
]
}
I'd like to iterate the users.
ActiveSupport::JSON.decode(response.body)["users"].each do |user|
puts user["name"]
end
As far as I understand, the problem is: ruby doesn't recognize ActiveSupport::JSON.decode(response.body)["users"] as an array, and thus puts returns me only the first user.
How do I solve that problem?
What you have pasted is not valid JSON. The trailing comma after on each "name" is a problem
"name": "Chris Rivers",
You'll get a LoadError trying to decode this with ActiveSupport::JSON.decode
MultiJson::LoadError: 399: unexpected token at '{"user_id": 1,"name": "Chris Rivers",},{"user_id": 3,"name": "Peter Curley",}]}'
If we clean up the JSON, turning it into something ActiveSupport::JSON.decode can understand
"{\"users\": [{\"user_id\": 1,\"name\": \"Chris Rivers\"},{\"user_id\": 3,\"name\": \"Peter Curley\"}]}"
you'll see there is no issue iterating over each object in "users" (x below is the above JSON string)
[8] pry(main)> ActiveSupport::JSON.decode(x)["users"].map { |user| user["name"] }
=> ["Chris Rivers", "Peter Curley"]
Does your source data actually have the trailing commas after each user's name? I get a parse error for that, but your code works as you want it to if I remove them:
json = '{ "users": [ { "user_id": 1, "name": "Chris Rivers" }, { "user_id": 3, "name": "Peter Curley" } ]}'
ActiveSupport::JSON.decode(json)["users"].each do |user|
puts user["name"]
end
The problem isn't not recognizing the array, it's the trailing commas after the "name" elements.
Removing those allows JSON parsing to proceed normally:
pry(main)> ActiveSupport::JSON.decode(s)["users"]
=> [{"user_id" => 1, "name" => "Chris Rivers"},
{"user_id" => 3, "name" => "Peter Curley"}]

Displaying an object with Ruby on Rails

I have some JSON that looks like this. I have it stored and read into an object, #items.
[
{
{
"id": "A",
"description": "a_description"
},
{
"id": "B",
"description": "b_description"
}
},
{
{
"id": "A",
"description": "a_description"
},
{
"id": "B",
"description": "b_description"
}
}
]
My goal is to display a table with two columns, one labeled A and the other labeled B, in which each row gives the "a_description" and "b_description". I'm not sure how to go about doing this.
Ah, the ol' array of hashes and hashes of arrays problem.
To get around your "out of order" problem you first have to convert
{
"id": "A",
"description": "foo"
},
{
"id": "B",
"description": "bar"
}
into {"A" : "foo", "B" : "bar" }.
#new_items = #items.map do |item|
output = {}
item.each do |hash|
output.merge!(hash["id"] => hash["description"])
end
end
Then #new_items becomes (intentionally presented out of order since hash elements are not ordered)
[
{
"A": "a1_description",
"B": "b1_description"
},
{
"B": "b2_description",
"A": "a2_description"
}
]
From there, each line is simply a hash, so you can just dereference the value you need based on the column you're in.
#new_items.each do |item|
puts "#{item['A']} is paired with #{item['B']}"
end
Keys, of course could be retrieved dynamically if you don't want to hard code "A" and "B" using .keys
Something like this maybe
<tr><th>A</th><th>B</th></tr>
<% #items.each do |item| %>
<tr><td><%=item[0].description%></td><td><%=item[1].description%></td></tr>
<% end %>

Resources