Extract specific field from JSON nested hashes - ruby-on-rails

I am thinking of writing a web application that crawls an API and returns this information in JSON form.
However, I am only after one number, then current price (in this sample, "227"). How can I access that in Ruby? I have no clue where to begin. I've never dealt with text like this.
For discussion's sake, suppose I save this output into instance variable #information
{
"item": {
"icon": "http://services.runescape.com/m=itemdb_rs/4332_obj_sprite.gif?id=4798",
"icon_large": "http://services.runescape.com/m=itemdb_rs/4332_obj_big.gif?id=4798",
"id": 4798,
"type": "Ammo",
"typeIcon": "http://www.runescape.com/img/categories/Ammo",
"name": "Adamant brutal",
"description": "Blunt adamantite arrow...ouch",
"current": {
"trend": "neutral",
"price": 227
},
"today": {
"trend": "neutral",
"price": 0
},
"day30": {
"trend": "positive",
"change": "+1.0%"
},
"day90": {
"trend": "positive",
"change": "+1.0%"
},
"day180": {
"trend": "positive",
"change": "+2.0%"
},
"members": "true"
}
}

First follow this post to parse this JSON in to Hash
Parsing a JSON string in Ruby
say the parsed hash name is my_hash then the following should give you price
my_hash['item']['current']['price']
Edit:
As you said you want to save it in #information
#information = my_hash['item']['current']['price']

Even you can use hashie it gives your json into readable structural code
Install Hashie
gem install hashie
then in your code all that json take in a variable my_json
myhash = Hashie::Mash.new(my_json)
#information = my_hash.item.current.price
Tips:-
if your json is dynamic and it may respond some other structural element so you can maintain exceptional code
#information = my_hash.item.try(:current).try(:price)

Related

Access Nested Netsuite Ruby Hash

I am trying to access a Netsuite Ruby Hash (Netsuite gem) and return orders which need updating from the db, the process uses two service objects, one to get the orders from db, (ListOrdersService), and the this file to compare those against modified ones in Netsuite. Is all working except I am having problems getting some of the nested values in Netsuite in to the output. Code is below with a troublesome item commented out. Its just an each method which compares dates then puts the needed orders in returned value.
def process_order_updates
get_order_updates = []
# Get all our open orders from DB
bj_open_orders = ListOrdersService.new.call
# Get all identical open orders from Netsuite
bj_open_orders.each do |item|
netsuite_sales_orders = NetSuite::Records::SalesOrder.get(item['sales_order_internal_id'])
# Compare the last modified date from Netsuite to the last checked date from app DB
if netsuite_sales_orders.present? && netsuite_sales_orders.last_modified_date > item['last_checked_date']
# If the last modified date is newer, then we create a new hash with the updated order info
get_order_updates << {
sales_order_internal_id: item['sales_order_internal_id'],
order_status: item['order_status']
# quantity_fulfilled: item['items_list']['item']['quantity_fulfilled']
}
puts "still open order #{item['sales_order_internal_id']} needs to be updated, it was last checked by at #{item['last_checked_date']} but it was just modified, on #{netsuite_sales_orders.last_modified_date}"
end
end
puts "Here are the orders that need to be updated: #{get_order_updates}"
end
The Netsuite file code I am referencing is below, and trying to get quantity_fulfilled, quantity_billed, and some others in the file. items_list is a top level item
"item_list": {
"list": [
{
"attributes": {
"item": {
"internal_id": "110",
"external_id": null,
"type": null,
"attributes": {
"name": "000002 Kerosene (UN1223) 3.PGIII (D/E)"
}
},
"expand_item_group": false,
"quantity": "1000.0",
"units": {
"internal_id": "1",
"external_id": null,
"type": null,
"attributes": {
"name": "ltr"
}
},
"description": "Kerosene (UN1223) 3.PGIII (D/E)",
"price": {
"internal_id": "-1",
"external_id": null,
"type": null,
"attributes": {}
},
"rate": "0.81",
"amount": "810.0",
"is_closed": false,
"gross_amt": "850.5",
"line": "1",
"cost_estimate_type": "_averageCost",
"cost_estimate": "900.79",
"quantity_back_ordered": "0.0",
"quantity_billed": "0.0",
"quantity_committed": "1000.0",
"quantity_fulfilled": "0.0",
"tax1_amt": "40.5",
"tax_code": {
"internal_id": "2214",
"external_id": null,
"type": null,
"attributes": {
"name": "VAT:RDR-5%"
}
},
Any tips on how to get those items, directly or with a hashmap welcome Thanks

Best Practice / Design Patterns for data reformat

My project is a micro-services that connect two major services, my project fetches data from one server, format the data, and then use the data to generate an XML file, and then upload the XML to another service. I'm just wondering if there is any design pattern for this kind of micro-services.
this is the JSON received from the backend server:
{
"employee_id": 100464,
"organization_id": 93,
"start_date": "2018-09-05",
"first_name": "Tom",
"departments": [
{
"id": 2761,
"name": "Sale",
"organization_id": 93
},
{
"id": 2762,
"name": "Product",
"organization_id": 93
}
],
"primary_department": {
"id": 2761,
"name": "Product",
"organization_id": 93
}
}
This is the data format I want for, so I need to do some data formatting:
{
"employee_id": 100464,
"organization_id": 93,
"first_name": "Tom",
"target_department": {
"department_id": 2761,
"name": "Product",
"organization_id": 93,
"is_primary_department": true
}
}
the logic to determine the target_department is
departments = hsh.delete :departments
primary_department = hsh.delete :primary_department
hsh[:target_department] = departments.select do |department|
department[:id] ==another_obj[:group_id]
end.first
hsh[:target_department][:is_home_department] = (hsh[:target_department][:id] == primary_department[:id])
hsh[:target_department][:department_id] = hsh[:target_department].delete :id
As you can see, I need to remove, rename, and reformat attributes and the structure of the data.
During the process, there are many potential issues: attributes not existed?
My question is what's the best practice for dealing with this issue from programming designing perspective?
I am using Rails, so any good gem or project is dealing with a similar issue?
I don't know about best practices, but this ought to do what you want.
{
"employee_id": 100464,
"organization_id": 93,
"start_date": "2018-09-05",
"first_name": "Tom",
"departments": [
{
"id": 2761,
"name": "Sale",
"organization_id": 93
},
{
"id": 2762,
"name": "Product",
"organization_id": 93
}
],
"primary_department": {
"id": 2761,
"name": "Product",
"organization_id": 93
}
}.with_indifferent_access.except(:start_date, :departments).transform_keys do |k|
k == 'primary_department' ? 'target_department' : k
end.tap do |hsh|
if hsh['target_department']
hsh['target_department']['is_primary_department'] = true
hsh['target_department']['department_id'] = hsh['target_department']['id']
hsh['target_department'].delete('id')
end
puts hsh
end
In console, this will return:
{
"employee_id"=>100464,
"organization_id"=>93,
"first_name"=>"Tom",
"target_department"=>{
"name"=>"Product",
"organization_id"=>93,
"is_primary_department"=>true,
"department_id"=>2761
}
}
BTW, in your example output, you show the target_department name as "aProduct". Is that a typo?
Working directly with json/xml type data programmatically is often tedious. I once worked at a company that did this EVERYWHERE and it was painful. I would suggest deserializing your data into an object graph representation. Also have a result class that can be constructed by querying/processing your input object graph. Then just serialize your result object back to json at the end.
You can likely find a built-in or open source solution for handling the serialization and deserialization for you.

serializing array when nested with other attributes in rails 5

I have a ruby (2.4.0p0) rails (5.0.2) controller from which I wish to return a json result containing a list of Thing objects as well as some high level info (such as next and previous from Kaminari paging).
Consider a Thing with an association to Owner. Thing has a owner_id attribute.
For #things = Thing.page(1).per(2) I will be able to use
render json: #things
and get;
[
{ "id": 1, "owner_id": 1, "name": "thing1" },
{ "id": 2, "owner_id": 1, "name": "thing2" }
]
Good. If I then create a serializer called ThingSerializer.rb and define owner such that it adds "owner":"CatInHat" instead of "owner_id":1
This works as well;
[
{ "id": 1, "owner": "CatInHat", "name": "thing1" },
{ "id": 2, "owner": "CatInHat", "name": "thing2" }
]
This is good, but, my problem comes when I want to add higher level data and label the list as "results" such as when I try;
render json: { next:"some_url_link",previous:"some_other_url_link", results: #bags}
I'd like to get;
{ "next":some_url_link,
"prev":some_other_url_link,
"results":[ { "id": 1, "owner": "CatInHat", "name": "thing1" }, { "id": 2, "owner": "CatInHat", "name": "thing2" } ]
}
What I get is nearly the above but with "owner_id":1 instead of "owner":"CatInHat" - my serializer does not seem to be used when I label and nest my list of things. What is the appropriate way to use my serializer and get this output?
If I create config/initializers/active_model_serializers.rb and add
ActiveModel::Serializer.config.adapter = :json_api
It gives me an api which is similar but I don't know if it can be customized to fit the spec I need above.
thank you for any help
It looks like the serialization logic in render json: ... only kicks in if the attribute is an ActiveRecord object or an array of ActiveRecord objects. Since you are giving it a hash, it will not inspect the individual attributes and recursively apply the serializers.
You can try manually applying ThingSerializer:
render json: {
next: ...,
prev: ...,
results: #things.map { |thing|
ThingSerializer.new(thing).attributes
},
}

Accessing Values in Deeply Nested Array - Ruby on Rails

I am using an API call which returns a JSON response. I want to access the data inside the response so I can create some nice display cards showing the info and pictures. Here is a snippet from the response, the response property is populated with about 20 objects I'll include just two for brevity:
{
"success": true,
"message": "",
"result": [
{
"MarketCurrency": "LTC",
"BaseCurrency": "BTC",
"MarketCurrencyLong": "Litecoin",
"BaseCurrencyLong": "Bitcoin",
"MinTradeSize": 1e-8,
"MarketName": "BTC-LTC",
"IsActive": true,
"Created": "2014-02-13T00:00:00",
"Notice": null,
"IsSponsored": null,
"LogoUrl": "https://i.imgur.com/R29q3dD.png"
},
{
"MarketCurrency": "DOGE",
"BaseCurrency": "BTC",
"MarketCurrencyLong": "Dogecoin",
"BaseCurrencyLong": "Bitcoin",
"MinTradeSize": 1e-8,
"MarketName": "BTC-DOGE",
"IsActive": true,
"Created": "2014-02-13T00:00:00",
"Notice": null,
"IsSponsored": null,
"LogoUrl": "https://i.imgur.com/e1RS4Hn.png"
},
In my Rails controller I'm using JSON.parse and I'm trying to turn it into an object with the Open struct option:
#markets = JSON.parse(markets.to_json, object_class: OpenStruct)
In my view I'll do this <%=#markets.class%> and it shows Array and not object. So I try this <%=#markets.size%> and it shows 1. If I do <%=#markets[0]['success']%> I would expect it to return true but it returns 'success'. So, I'm not understanding why the ostruct library isn't working like I would expect or how I can get to the objects stored in the result array. Any help is greatly appreciated!
You already have a JSON response, isn't needed to use to_json again, try just parsing that object, and then use the dot . to access its fields, as an OpenStruct object now then you can access them as methods:
require 'json'
a = '{
"success": true,
"message": "",
"result": [{
"MarketCurrency": "LTC",
"BaseCurrency": "BTC",
"MarketCurrencyLong": "Litecoin",
"BaseCurrencyLong": "Bitcoin",
"MinTradeSize": 1e-8,
"MarketName": "BTC-LTC",
"IsActive": true,
"Created": "2014-02-13T00:00:00",
"Notice": null,
"IsSponsored": null,
"LogoUrl": "https://i.imgur.com/R29q3dD.png"
}, {
"MarketCurrency": "DOGE",
"BaseCurrency": "BTC",
"MarketCurrencyLong": "Dogecoin",
"BaseCurrencyLong": "Bitcoin",
"MinTradeSize": 1e-8,
"MarketName": "BTC-DOGE",
"IsActive": true,
"Created": "2014-02-13T00:00:00",
"Notice": null,
"IsSponsored": null,
"LogoUrl": "https://i.imgur.com/e1RS4Hn.png"
}]
}'
b = JSON.parse(a, object_class: OpenStruct)
p b.success
# => true
After much debugging and some assistance, I was able to get it to work. The response from the API call was an array with one item. The item was a long string of the entire dataset.
In order to get the expected behavior of "true" when calling #markets.success, I first had to
raw_markets = JSON.parse(markets.to_json)
followed by
#markets = raw_markets.map do |market|
JSON.parse(market, object_class: OpenStruct)
Note: the variable markets holds the original api call:
markets = open('url-to-api')
After this I would get #markets.success = "true" and #markets.result[0] held the first result, #markets.result[1] held the second result, and so on.

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

Resources