display nested JSON in ruby on rails - ruby-on-rails

I believe this is going to be an easy fix but everything I'm trying isn't working.
I'm getting data from an external API and I'm looking at display the data after matching the ids.
The data is JSON and has some nested content.
I've tried the following
<li>Street: <%= #users.find{|u| u['id'] == #album['userId']}.try(:[],'address''street') || 'not available' %></li>
The data is structured like so
{
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere#april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
}

You'll want that to be:
<li>Street: <%= #users.find{|u| u['id'] == #album['userId']}.try(:[],'address').try(:[],'street') || 'not available' %></li>
For a multi-level try, you might want to look into dig.
Also, if the iteration is as in your previous questions, then #album may need to be album. If this is in a different context than your previous questions, then kindly disregard.

This is where dig is useful:
https://ruby-doc.org/core-2.3.0_preview1/Hash.html
<li>Street: <%= #users.find{|u| u['id'] == #album['userId']}.dig(:address, :street) || 'not available' %></li>

Related

how to read individual data from the content of remote JSON file in Ruby on Rails?

I am trying to read individual data from the content of json API on Oil and Gas Authority website; however the code I have returns all the data. Any help would be highly appreciated.
require 'json'
require 'open-uri'
def index
url='http://data-ogauthority.opendata.arcgis.com/datasets/ab4f6b9519794522aa6ffa6c31617bf8_0.geojson'
#result = JSON.parse open(url).read
end
This my index view:
<% #result.each do |row| %>
<%= row %>
<% end %>
Given that the API (as you are currently using it) returns a JSON structure like this:
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"properties":{
"FIELDNAME":"GRYPHON",
"FIELDTYPE":"OIL",
"NAME_SHORT":"GRYPHON",
"STATUS":"PRODUCING",
"DISC_DATE":"1987/07",
"DISC_WELL":"9/18b-7",
"STAT_CODE":"800",
"PROD_DATE":"1993/10",
"DEPTH_M":"111.86",
"DET_STATUS":"DETERMINED",
"ORIG_OP":"KERR-MCGEE",
"ORIG_LIC":"P.496",
"ORIG_LIC_2":"P.478",
"ORIG_LIC_3":"P.257",
"ORIG_LIC_4":"P.103",
"ORIG_LIC_5":" ",
"CURR_OPER":"MAERSK OIL NORTH SEA UK LIMITED",
"FIELDDATA":"https://itportal.decc.gov.uk/fields/fields_index/ukcs+field+information+listed+by+field+name/183.htm",
"OBJECTID":16,
"OGA_COP":null
},
"geometry":{
"type":"Polygon",
"coordinates":[
[
[1.5701447246411744,59.35253688325039],
...
]
]
}
},
...
]
}
You could do something like:
<% #result[:features].each do |feature| %>
<%= feature[:properties][:FIELDNAME] %>
<%= feature[:properties][:FIELDTYPE] %>
...
<% end %>
Your JSON file looks to be something like 1.3MB. So, unless you can figure out how to filter your results on the API side (using query params, I would suppose), you may end up with various performance issues in retrieving the JSON.
And, you may want to do:
#result = JSON.parse(open(url).read).with_indifferent_access
So that you can use symbols to access hash elements as well as strings.
One thing to add to #jvillian answer is that if one of the keys is nil then calling this branch's subsequent keys will result in undefined method '[]'. Ruby 2.3+ has a new method called dig which will simply return nil. More on dig in my answer to this question.
Also to add to #jvillian answer, you can fetch the filtered information using this link; for example, taking into account the fields you need:
FIELDNAME
FIELDTYPE
STATUS
DISC_DATE
DISC_WELL
You could create query that will result in the following response:
{
"displayFieldName": "FIELDNAME",
"fieldAliases": {
"FIELDNAME": "Field Name",
"FIELDTYPE": "Field Type",
"STATUS": "Status",
"DISC_DATE": "Discovery Date",
"DISC_WELL": "Discovery Well"
},
"fields": [
{
"name": "FIELDNAME",
"type": "esriFieldTypeString",
"alias": "Field Name",
"length": 32
},
{
"name": "FIELDTYPE",
"type": "esriFieldTypeString",
"alias": "Field Type",
"length": 4
},
{
"name": "STATUS",
"type": "esriFieldTypeString",
"alias": "Status",
"length": 50
},
{
"name": "DISC_DATE",
"type": "esriFieldTypeString",
"alias": "Discovery Date",
"length": 20
},
{
"name": "DISC_WELL",
"type": "esriFieldTypeString",
"alias": "Discovery Well",
"length": 20
}
],
"features": [
{
"attributes": {
"FIELDNAME": "GRYPHON",
"FIELDTYPE": "OIL",
"STATUS": "PRODUCING",
"DISC_DATE": "1987/07",
"DISC_WELL": "9/18b-7"
}
},
{
"attributes": {
"FIELDNAME": "BRAEMAR",
"FIELDTYPE": "COND",
"STATUS": "PRODUCING",
"DISC_DATE": "1995/05",
"DISC_WELL": "16/03b-8Z"
}
},
...
]
}
This file will be now 76KB, and you can extract the data in almost the same way, just change properties for attributes, i.e.:
<% #result[:features].each do |feature| %>
<%= feature[:attributes][:FIELDNAME] %>
<%= feature[:attributes][:FIELDTYPE] %>
...
<% end %>

How do I safely parse data from a Ruby Hash?

I'm parsing a JSON result into a Ruby hash. The JSON result looks like this:
{
"records": [
{
"recordName": "7DBC4FAD-D18C-476A-89FB-14A515098F34",
"recordType": "Media",
"fields": {
"data": {
"value": {
"fileChecksum": "ABCDEFGHIJ",
"size": 9633842,
"downloadURL": "https://cvws.icloud-content.com/B/ABCDEF"
},
"type": "ASSETID"
}
},
"recordChangeTag": "ii23box2",
"created": {
"timestamp": 1449863552482,
"userRecordName": "_abcdef",
"deviceID": "12345"
},
"modified": {
"timestamp": 1449863552482,
"userRecordName": "_abcdef",
"deviceID": "12345"
}
}
]
}
I can't guarantee that it'll return with any/all those values, or that each value will be of a certain type (e.g. Array, Hash, string, number), and if I call it incorrectly then I get a crash.
Right now I need the downloadURL for the first item in the 'records' array, or to write it as I might with the Swift library SwiftyJSON (which I'm far more familiar with):
json["records"][0]["fields"]["data"]["value"]["downloadURL"]
I'm wondering what the safest/best/standard way to do this safely in Ruby is. Perhaps I'm thinking about it wrong?
In ruby 2.3 and above you can use Hash#dig and Array#dig
json = JSON.parse(...)
json.dig('records', 0, 'fields', 'data', 'value', 'downloadURL')
You'll get nil if any of the intermediate values is nil. If one of the intermediate values doesn't have a dig method, for example if `json['records'][0]['fields'] was unexpectedly an integer this will raise TypeError.
From the documentation (http://ruby-doc.org/stdlib-2.2.3/libdoc/json/rdoc/JSON.html):
require 'json'
my_hash = JSON.parse('{"hello": "goodbye"}')
puts my_hash["hello"] => "goodbye"
If you're worried that you might not have some data. See this question:
Equivalent of .try() for a hash to avoid "undefined method" errors on nil?
You can recursively search each object contained in the json object using
the recurse_proc method of the JSON module.
Here is an example using the data you provided.
require 'json'
json_string = '{
"records": [
{
"recordName": "7DBC4FAD-D18C-476A-89FB-14A515098F34",
"recordType": "Media",
"fields": {
"data": {
"value": {
"fileChecksum": "ABCDEFGHIJ",
"size": 9633842,
"downloadURL": "https://cvws.icloud-content.com/B/ABCDEF"
},
"type": "ASSETID"
}
},
"recordChangeTag": "ii23box2",
"created": {
"timestamp": 1449863552482,
"userRecordName": "_abcdef",
"deviceID": "12345"
},
"modified": {
"timestamp": 1449863552482,
"userRecordName": "_abcdef",
"deviceID": "12345"
}
}
]
}'
json_obj = JSON.parse(json_string)
JSON.recurse_proc(json_obj) do |obj|
if obj.is_a?(Hash) && obj['downloadURL']
puts obj['downloadURL']
end
end
Update Based on Frederick's answer and Cary's comment
I originally assumed you just wanted to find the downloadURL somewhere in the json without crashing, but based on Frederick's answer and Cary's comment, it's reasonable to assume that you only want to find the downloadURL if it is at the exact path, rather than if it just exists. Building on Frederick's answer and Cary's comment here are a couple of other options that should safely find the downloadURL at the expected path.
path = ['records', 0, 'fields', 'data', 'value', 'downloadURL']
parsed_json_obj = JSON.parse(json_string)
node_value = path.reduce(parsed_json_obj) do |json,node|
if json.is_a?(Hash) || (json.is_a?(Array) && node.is_a?(Integer))
path = path.drop 1
json[node]
else
node unless node == path.last
end
end
puts node_value || "not_found"
path = ['records', 0, 'fields', 'data', 'value', 'downloadURL']
begin
node_value = parsed_json_obj.dig(*path)
rescue TypeError
node_value = "not_found"
end
puts node_value || "not_found"
BTW, this assumes the json is at least valid, if that is not a given you might want to wrap the JSON.parse in a begin-rescue-end block as well.

Searching nested hash

These is sample response of hashes in ruby.
Eg:-
find abcd1234
should give me
i was able to find by but it's not sufficent
I have response of sth like these and list keep on going different value but same structure
[
{
"addon_service": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"name": "heroku-postgresql"
},
"config_vars": [
"FOO",
"BAZ"
],
"created_at": "2012-01-01T12:00:00Z",
"id": "01234567-89ab-cdef-0123-456789abcdef",
"name": "acme-inc-primary-database",
"plan": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"name": "heroku-postgresql:dev"
},
"app": {
"id"=>"342uo23iu4io23u4oi2u34",
"name"=>"heroku-staging"},
},
"provider_id": "abcd1234",
"updated_at": "2012-01-01T12:00:00Z",
"web_url": "https://postgres.heroku.com/databases/01234567-89ab-cdef-0123-456789abcdef"
} .........
]
can anyone know how to grab those?
You can iterate all array element (a hash) and display its content if the hash meet your requirement:
element_found = 0
YOUR_DATA.each do |element|
if element["provider_id"].match(/abcd1234/)
element_found += 1
puts "addon_service: #{element['addon_service']['name']}"
puts "app: #{element['app']['name']}"
end
end
if element_found == 0 puts "Sorry match didn't found"
Since the elements of the array are hashes you can select the appropriate ones by matching the desired key.
select {|app| app[:provider_id] == "abcd1234"}
Do you know what to do with the element once you select it?
I think you want some of the items from the hash, but not all of them.
That might look like:
select {|app| app[:provider_id] == "abcd1234"}.map {|app| app.select {|key, v| [:addon_service, :app].include?(key) } }

Rails errors - Define Position or Object in which the error occured

I have two models. For example Person and Address.
Because I want to add or update addresses to the person within one request, the person model looks like:
has_many :addresses
accepts_nested_attributes_for :addresses
In the address controller is only one validation
validates :city, presence: true
When I now update the user via json api it works like a charm:
{
"user": {
"addresses_attributes": [
{"street": "bla", "zip": "12345", "city": "blubb"},
{"street": "blu", "zip": "98765", "city": "blebb"}
]
}
}
Now I delete the city of the second record:
{
"user": {
"addresses_attributes": [
{"street": "bla", "zip": "12345", "city": "blubb"},
{"street": "blu", "zip": "98765"}
]
}
}
and in the Users controller I can render a json response, something like:
render json: #user.errors
which gives me the correct error.
I am missing, that I don't know which of the addresses threw the error (In this example the second).
Any Ideas?
You can return the entire user object with its nested attributes and errors. I.e.
render json: #user.as_json(
include: [{addresses: {methods: [:errors]}],
methods: [:errors]
)
The result should look like this:
{
"user": {
"errors": {...},
"addresses_attributes": [
{"street": "bla", "zip": "12345", "city": "blubb", "errors": {...}},
{"street": "blu", "zip": "98765", "errors": {...}}
]
}
}
The #user that you tried to create will still contain the addresses that it tried to create with the nested attributes.
I don't know exactly how you want to render the fact that an address failed the validation but you can identify the one(s) that did fail by iterating over the #user.addresses.
For example, this will return all the invalid addresses:
#user.addresses.select { |address| !address.valid? }
You can still render these objects, or the json representation of them, even though they haven't been saved to the database.

Rails Backbone Render Nested JSON in jst.eco template

I have this set JSON data
JSON
[{
"country": {
"name": "Malaysia",
"total_amount": 0.0,
"count": 0
}
}, {
"country": {
"name": "Philippines",
"total_amount": 0.0,
"count": 0
}
}, {
"country": {
"name": "Thailand",
"total_amount": 0.0,
"count": 0
}
}]
Let say this data I sent it like
.replaceWith(#template(data: #data_transaction)
How do I retrieve the data inside the template.jst.eco
I have tried to use this kind of for loop
<% for key, data in #data_transaction.models: %>
<%= data.get("country").name %>
<%= data.get("country").total_amount %>
<%= data.get("country").count %>
it just does not work
if I tried to print it out this way inside the template.jst.eco
<%= #data_transaction %>
it will show this kind of things
[object, object]
Any helps?
Thank you very much
I fear you made a very common mistake.
Try to replace:
for key, data in #data_transaction.models:
with:
for object in #data_transaction.models:
for key, data of object:

Resources