Ruby - Trying to iterate through a hash with a nested hash (after desieralising a JSON object) - json

I'm quite new to Ruby and I thought I'd write a program that would call an API and deserailise the JSON object.
For reference, here is the API I am calling; https://api.exchangeratesapi.io/latest?base=GBP
I'm having trouble iterating through the hash, I get the following error:
Traceback (most recent call last):
2: from ruby-api.rb:21:in `<main>'
1: from ruby-api.rb:21:in `each'
ruby-api.rb:23:in `block in <main>': undefined method `each' for "GBP":String (NoMethodError)
Here is the code that I have so far (with comments)
#requiring the library for http client
require 'net/http'
#require the URI library
require 'uri'
#requiring the json library for deserialising JSON objects
require 'json'
#output of get request stored in a string to deserialise JSON
json_string = Net::HTTP.get(URI.parse('https://api.exchangeratesapi.io/latest?base=GBP'))
#testing output
puts "Direct output \n#{json_string}"
puts "First JSON Parse attempt\n"
JSON.parse(json_string).each do |currency, rate|
puts "#{currency} - #{rate}"
end
#parse the JSON object to a hash and iterate through it, displaying each key, value pair
puts "JSON Parse output with nested each statements \n"
JSON.parse(json_string).each do |key, value|
#however - this contains a nested hash! So a nested each do is needed.
value.each do |currency, rate|
puts "#{currency} - #{rate}"
end
end
Here's the full output for when I run the code:
Direct output
{"rates":{"CAD":1.6409680499,"HKD":9.6691560447,"ISK":152.3948565332,"PHP":63.800325678,"DKK":8.3851984951,"HUF":373.6986916727,"CZK":28.9078555786,"GBP":1.0,"RON":5.3321354371,"SEK":12.1404907631,"IDR":17442.1809197597,"INR":87.4181593576,"BRL":5.0228536133,"RUB":79.8944353978,"HRK":8.3345499467,"JPY":131.652535235,"THB":37.5506766242,"CHF":1.2255601101,"EUR":1.1230276826,"MYR":5.16064911,"BGN":2.1964175417,"TRY":7.0194845303,"CNY":8.8154304004,"NOK":11.220731091,"NZD":1.9484530294,"ZAR":18.6923465663,"USD":1.2329720928,"MXN":24.1548655174,"SGD":1.7001516087,"AUD":1.824583076,"ILS":4.2960300971,"KRW":1473.7716884721,"PLN":4.8565332135},"base":"GBP","date":"2019-10-04"}
First JSON Parse attempt
rates - {"CAD"=>1.6409680499, "HKD"=>9.6691560447, "ISK"=>152.3948565332, "PHP"=>63.800325678, "DKK"=>8.3851984951, "HUF"=>373.6986916727, "CZK"=>28.9078555786, "GBP"=>1.0, "RON"=>5.3321354371, "SEK"=>12.1404907631, "IDR"=>17442.1809197597, "INR"=>87.4181593576, "BRL"=>5.0228536133, "RUB"=>79.8944353978, "HRK"=>8.3345499467, "JPY"=>131.652535235, "THB"=>37.5506766242, "CHF"=>1.2255601101, "EUR"=>1.1230276826, "MYR"=>5.16064911, "BGN"=>2.1964175417, "TRY"=>7.0194845303, "CNY"=>8.8154304004, "NOK"=>11.220731091, "NZD"=>1.9484530294, "ZAR"=>18.6923465663, "USD"=>1.2329720928, "MXN"=>24.1548655174, "SGD"=>1.7001516087, "AUD"=>1.824583076, "ILS"=>4.2960300971, "KRW"=>1473.7716884721, "PLN"=>4.8565332135}
base - GBP
date - 2019-10-04
JSON Parse output with nested each statements
CAD - 1.6409680499
HKD - 9.6691560447
ISK - 152.3948565332
PHP - 63.800325678
DKK - 8.3851984951
HUF - 373.6986916727
CZK - 28.9078555786
GBP - 1.0
RON - 5.3321354371
SEK - 12.1404907631
IDR - 17442.1809197597
INR - 87.4181593576
BRL - 5.0228536133
RUB - 79.8944353978
HRK - 8.3345499467
JPY - 131.652535235
THB - 37.5506766242
CHF - 1.2255601101
EUR - 1.1230276826
MYR - 5.16064911
BGN - 2.1964175417
TRY - 7.0194845303
CNY - 8.8154304004
NOK - 11.220731091
NZD - 1.9484530294
ZAR - 18.6923465663
USD - 1.2329720928
MXN - 24.1548655174
SGD - 1.7001516087
AUD - 1.824583076
ILS - 4.2960300971
KRW - 1473.7716884721
PLN - 4.8565332135
Traceback (most recent call last):
2: from ruby-api.rb:21:in `<main>'
1: from ruby-api.rb:21:in `each'
ruby-api.rb:23:in `block in <main>': undefined method `each' for "GBP":String (NoMethodError)
So what's happening is that it will happily iterate through the hash but as soon as it hits the 'base' key (with a value of GBP) - it falls over.
What's the best way to resolve this?
Thanks!

You're trying to iterate over each value in the parsed JSON, which works in the same time because the value of rates is a hash, but the values for base and date are strings. There you invoke each and get the NoMethodError exception.
Try instead accessing the dates key:
JSON.parse(json_string)['rates'].each do |currency, rate|
puts "#{currency} - #{rate}"
end

Related

json schema validation does not work for dependencies?

As far as I understand, using:
dependencies:
cake:
- eggs
- flour
in a JSON schema validation (I actually parse the schema in YAML, but it should not really matter) should forbid the presence of a cake without an entry for eggs and flour. So this should be rejected:
receip:
cake: crepes
while this should be accepted:
receip:
eggs: 3 eggs, given by farmer
flour: 500g
cake: crepes
Unfortunately, both cases are accepted in my case. Any idea what I made wrong? I also tried to add another level required as proposed by jsonSchema attribute conditionally required but the problem is the same.
MWE
In case it helps, here are all the files that I'm using.
debug_schema.yml:
$schema: https://json-schema.org/draft/2020-12/schema
type: object
properties:
receip:
type: object
properties:
eggs:
type: string
flour:
type: string
cake:
type: string
dependencies:
cake:
- eggs
- flour
b.yml:
receip:
## I'd like to forbid a cake without eggs and flour
## Should not validate until I uncomment:
# eggs: 3 eggs, given by farmer
# flour: 500g
cake: crepes
validate_yaml.py
#!/usr/bin/env python3
import yaml
from jsonschema import validate, ValidationError
import argparse
# Instantiate the parser
parser = argparse.ArgumentParser(description='This tool not only checks if the YAML file is correct in term'
+ ' of syntex, but it also checks if the structure of the YAML file follows'
+ ' the expectations of this project (e.g. that it contains a list of articles,'
+ ' with each articles having a title etc…).')
# Optional argument
parser.add_argument('--schema', type=str, default="schema.yml",
help='Path to the schema')
parser.add_argument('--yaml', type=str, default="science_zoo.yml",
help='Path to the YAML file to verify')
args = parser.parse_args()
class YAMLNotUniqueKey(Exception):
pass
# By default, PyYAML does not raise an error when two keys are identical, which is an issue here
# see https://github.com/yaml/pyyaml/issues/165#issuecomment-430074049
class UniqueKeyLoader(yaml.SafeLoader):
def construct_mapping(self, node, deep=False):
mapping = set()
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
if key in mapping:
raise YAMLNotUniqueKey(f"Duplicate key {key!r} found.")
mapping.add(key)
return super().construct_mapping(node, deep)
with open(args.schema, "r") as schema_stream:
try:
schema = yaml.load(schema_stream, UniqueKeyLoader)
with open(args.yaml, "r") as yaml_stream:
try:
validate(yaml.load(yaml_stream, UniqueKeyLoader), schema)
print("The YAML file is correct and follows the schema, congrats! :D")
except ValidationError as exc:
print(exc.message)
print("The problem is located in the JSON at the path {}.".format(exc.json_path))
except YAMLNotUniqueKey as exc:
print("ERROR: {}".format(exc))
print("Rename in the YAML one the two instances of this key to ensure all keys are unique.")
except yaml.YAMLError as exc:
print("Errors when trying to load the YAML file:")
print(exc)
except YAMLNotUniqueKey as exc:
print("ERROR: {}".format(exc))
print("Rename in the schema one the two instances of this key to ensure all keys are unique.")
except yaml.YAMLError as exc:
print("Errors when trying to load the schema file:")
print(exc)
Command:
./validate_yaml.py --yaml b.yml --schema debug_schema.yml
Oh, so it seems that this keyword has been deprecated, and split into dependentRequired (see e.g. this example) and dependentSchemas (see e.g. this example). Just using dependentRequired solves the issue:
$schema: https://json-schema.org/draft/2020-12/schema
type: object
properties:
receip:
type: object
properties:
eggs:
type: string
flour:
type: string
cake:
type: string
dependentRequired:
cake:
- eggs
- flour

Invalid JSON Expression

I am making use of non-static import
JsonPath jp = response.jsonPath();
System.out.println(jp.get("data?(#.id>14).employee_name").toString());
For a JSON as shown below:
{"status":"success","data":[{"id":"1","employee_name":"Tiger Nixon","employee_salary":"320800","employee_age":"61","profile_image":""},{"id":"2","employee_name":"Garrett Winters","employee_salary":"170750","employee_age":"63","profile_image":""}]}
When i am trying to run it , i am getting below error:
java.lang.IllegalArgumentException: Invalid JSON expression:
Script1.groovy: 1: expecting EOF, found '[' # line 1, column 31.
data[?(#.id>14)].employee_name
^
1 error
Can someone guide me why is this error being thrown ?
I doubt if that syntax is right, nonetheless, you should be using the below
Also note that the id is a string in your response so you will have to include it in quotes
js.get("data.find {it.id > '14'}.employee_name").toString();

Problems Parsing a Json File in Ruby

I'm pretty new to programming ruby and using json packages, I think I'm facing a typical noob-error, but I just cant find my mistake for two days now.. I've started like this:
require 'discogs-wrapper'
require 'json'
aw = Discogs::Wrapper.new("my_application", user_token: "my_user_token")
inv = aw.get_user_inventory('my_user_name', :per_page => 1, :page => 1)
p=JSON.parse(inv)
What I recieve is this:
C:/Ruby24-x64/lib/ruby/2.4.0/json/common.rb:156:in `initialize': no implicit conversion of Hashie::Mash into String (TypeError)
from C:/Ruby24-x64/lib/ruby/2.4.0/json/common.rb:156:in `new'
from C:/Ruby24-x64/lib/ruby/2.4.0/json/common.rb:156:in `parse'
from C:/Users/rtuz2th/.RubyMine2017.3/config/scratches/scratch.rb:6:in `<top (required)>'
from -e:1:in `load'
from -e:1:in `<main>'
I've already been searching your forum and I've learned that this error probably means that I do not have a correct JSON-file (although it comes straight from the discogs-api, I basically thought these guys know what they do). So what I tried next was
require 'discogs-wrapper'
require 'json'
aw = Discogs::Wrapper.new("my_application", user_token: "my_user_token")
inv = aw.get_user_inventory('my_user_name', :per_page => 1, :page => 1)
inv = inv.to_json
p=JSON.parse(inv)
So at least, my code runs without any errors now. But when I try to refer to any information in inv, I just get empty responses. inv (without the .to_json looks like this:
{"pagination"=>
{"per_page"=>1,
"items"=>13692,
"page"=>1,
"urls"=>
{"last"=>
"URL containing tokens",
"next"=>
"URL containing tokens"},
"pages"=>13692},
"listings"=>
[{"status"=>"Draft",
"original_price"=>
{"curr_abbr"=>"EUR",
"formatted"=>"\u20AC10.46",
"value"=>10.46,
"curr_id"=>3},
"weight"=>230.0,
"original_shipping_price"=>
{"curr_abbr"=>"EUR",
"formatted"=>"\u20AC5.90",
"value"=>5.9,
"curr_id"=>3},
"price"=>{"currency"=>"EUR", "value"=>10.46},
"allow_offers"=>true,
"uri"=>"https://www.discogs.com/sell/item/437965910",
"sleeve_condition"=>"Very Good Plus (VG+)",
"format_quantity"=>1,
"id"=>437965910,
"shipping_price"=>{"currency"=>"EUR", "value"=>5.9},
"posted"=>"2017-02-07T23:43:01-08:00",
"ships_from"=>"Germany",
"in_cart"=>false,
"comments"=>"",
"seller"=>
{"username"=>"zweischeiben.de",
"stats"=>{"rating"=>"100.0", "total"=>143, "stars"=>5.0},
"uid"=>3359767,
"url"=>"https://api.discogs.com/users/zweischeiben.de",
"html_url"=>"https://www.discogs.com/user/zweischeiben.de",
"shipping"=>
"(Lots of information about shipping, cut just cut it out)"
"payment"=>"Bank Transfer, PayPal",
"avatar_url"=>
"(cut out url)",
"resource_url"=>"https://api.discogs.com/users/zweischeiben.de",
"id"=>3359767},
"condition"=>"Near Mint (NM or M-)",
"release"=>
{"thumbnail"=>
"(cut out url)",
"description"=>"Steamhammer - Mountains (LP, Album, RE)",
"artist"=>"Steamhammer",
"format"=>"LP, Album, RE",
"resource_url"=>"https://api.discogs.com/releases/7303333",
"title"=>"Mountains",
"year"=>0,
"id"=>7303333,
"catalog_number"=>"201.006, 0201.006"},
"resource_url"=>"https://api.discogs.com/marketplace/listings/437965910",
"audio"=>false,
"external_id"=>"3, 16",
"location"=>"T52574"}]}
But if I now try to refer to eg the location:
require 'discogs-wrapper'
require 'json'
aw = Discogs::Wrapper.new("my_application", user_token: "my_token")
inv=aw.get_user_inventory('my_username', :per_page => 1, :page => 1)
inv=inv.to_json
p=JSON.parse(inv)
puts p[location]
I'm just getting an error like this
C:\Ruby24-x64\bin\ruby.exe -e $stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) C:/Users/rtuz2th/.RubyMine2017.3/config/scratches/scratch.rb
C:/Users/rtuz2th/.RubyMine2017.3/config/scratches/scratch.rb:9:in `<top (required)>': undefined local variable or method `location' for main:Object (NameError)
from -e:1:in `load'
from -e:1:in `<main>'
Process finished with exit code 1
or an empty response. I'm trying to solve this for two days now, but I'm absolutely out of ideas. Thanks for your help in advance!
inv = inv.to_json
p = JSON.parse(inv)
The above is a noop. Use inv as is, it’s a hash already.
puts p[location]
You cannot just lookup whatever key located deeply in the tree hierarchy or you hash. You hash has stings as keys, it has a two top-level elements, 'pagination' and 'listings', the latter is an array, and each element of this array is a hash having location key.
While you were cutting the long value, you dropped the ending comma, making me unable to copy-paste the input and provide the exact answer.

undefined method `+#' for nil:NilClass rails after moving to mysql

I moved the database to mysql which is on VM. After moving database I started getting this error when I am trying to access or saving data to database but some models updation are working fine.
The model I am accessing has some arithmetic computations which I feel is the reason for this issue.
Error is
undefined method `+#' for nil:NilClass
I tried from rails console as well and I am getting same error with this command
Settings.all
I guess this is part of that method method
if params[:lat] and params[:lon] and params[:rad] and !params[:lat].empty? and
!params[:lon].empty? and !params[:rad].empty?
lat = params[:lat].to_f
lon = params[:lon].to_f
rad = params[:rad].to_f
ear = 6371.00
min_lat = (lat) - (rad/ear)/180.0*Math::PI;
max_lat = (lat) + (rad/ear)/180.0*Math::PI;
min_lon = (lon) - (rad/ear/Math.cos(lon*Math::PI/180.0))/180.0*Math::PI;
max_lon = (lon) + (rad/ear/Math.cos(lon*Math::PI/180.0))/180.0*Math::PI;
#settings = #settings.includes(:settings_location)
.where("settings_locations.latitude > :min_lat AND " \
"settings_locations.latitude < :max_lat AND " \
"settings_locations.longitude > :min_lon AND " \
"settings_locations.longitude < :max_lon", {
min_lat: min_lat,
max_lat: max_lat,
min_lon: min_lon,
max_lon: max_lon});
stack trace
Started POST "/settings.json" for 127.0.0.1 at 2014-04-24 13:52:53 +0200
NoMethodError - undefined method `+#' for nil:NilClass:
app/models/settings.rb:54:in `<top (required)>'
activesupport (4.0.2) lib/active_support/dependencies.rb:424:in `block in load_file'
activesupport (4.0.2) lib/active_support/dependencies.rb:616:in `new_constants_in'
activesupport (4.0.2) lib/active_support/dependencies.rb:423:in `load_file'
activesupport (4.0.2) lib/active_support/dependencies.rb:324:in `require_or_load'
activesupport (4.0.2) lib/active_support/dependencies.rb:463:in `load_missing_constant'
setting.rb:
class Settings < ActiveRecord::Base
has_one :settings_location
def to_builder
Jbuilder.new do |json|
json.location settings_location.to_builder
end
end //this line is 54
Per the comments, there seem to be only a handful of cases where you can reproduce the same error message in IRB. The first is:
foo, bar = nil, nil
foo.send("+#", bar) # undefined method `+#' for nil:NilClass
The second is:
foo = nil
+foo # undefined method `+#' for nil:NilClass
In an eval context, the latter might more look like:
foo, bar = nil, nil
eval("foo = +#{bar.inspect}") # undefined method `+#' for nil:NilClass
My personal guess is that your problem is something along the lines of the latter. Not with eval, but rather with one of its plethora of Ruby variations such as define_method. The actual call is very likely somewhere in Rail's internals and related to some uninitialized variable — perhaps MySQL is returning null where SQLite was returning something due to a recent schema change that you didn't think about?
Personally, I'd try to narrow it down step by step:
1. Comment out:
json.location settings_location.to_builder
If the error is gone, then:
2.a) Replace it with:
builder = settings_location.to_builder
json.location builder
If the error message is still the same, then:
2.b) Try
Jbuilder.new
… without a block.
The point is, you want to narrow things down to the specific method that is causing the error, and then dive into that method's code to see what is nil and shouldn't be. (Perhaps the block is missing some kind of return value?)

Bitcoind JSON comment error

Trying to add a comment (in json) to a transaction but keep getting this error: "error: value is type obj, expected int"
bitcoind move acc1 acc2 0.00000002 '{"test":"hi"}'
I was missing the minconf=1 arg value (ie. needed a 1):
bitcoind move acc1 acc2 0.00000002 1 '{"test":"hi"}'