I am getting the following data after API request and not sure how to get link from the data. Anyone help?
#<ExampleApi::Results::GetTemporaryLinkResult:0x00007f9edf280c78
#data={"metadata"=>{"name"=>"test.jpg", "id"=>"id:xxxxxxxx",
"link"=>"https://example.com/apitl/1/AnHQ08SIuxh9cdXL1q-EpG8L"}>
Its a Hash and you can retrieve by the key
#data["metadata"]["link"]
Edited
class ExamplerResult
attr_accessor :data
def initialize()
#data = {"metadata"=>{"name"=>"test.jpg", "id"=>"id:xxxxxxxx", "link"=>"https://example.com/apitl/1/AnHQ08SIuxh9cdXL1q-EpG8L"}}
end
end
data = ExamplerResult.new.data
data["metadata"]["link"]
For Hash you can use dig from ruby 2.3
#data.dig("metadata", "link") to retrieve (nested) keys
Reference link
https://ruby-doc.org/core-2.3.0_preview1/Hash.html#method-i-dig
Related
I'm having problems with weird behaviour in RoR. I'm having a Hash that i'm converting to json using to_json() like so:
data = Hash.new
# ...
data = data.to_json()
This code appears inside a model class. Basically, I'm converting the hash to JSON when saving to database. The problem is, the string gets saved to database with its surrounding quotes. For example, saving an empty hash results in: "{}". This quoted string fails to parse when loading from the database.
How do I get rid of the quotes?
The code is:
def do_before_save
#_data = self.data
self.data = self.data.to_json()
end
EDIT:
Due to confusions, I'm showing my entire model class
require 'json'
class User::User < ActiveRecord::Base
after_find { |user|
user.data = JSON.parse(user.data)
}
after_initialize { |user|
self.data = Hash.new unless self.data
}
before_save :do_before_save
after_save :do_after_save
private
def do_before_save
#_data = self.data
self.data = self.data.to_json()
end
def do_after_save
self.data = #_data
end
end
The data field is TEXT in mysql.
I'm willing to bet money that this is the result of you calling .to_json on the same data twice (without parsing it in between). I've had a fair share of these problems before I devised a rule: "don't mutate data in a lossy way like this".
If your original data was {}, then first .to_json would produce "{}". But if you were to jsonify it again, you'd get "\"{}\"" because a string is a valid json data type.
I suggest that you put a breakpoint in your before_save filter and see who's calling it the second time and why.
Update
"call .to_json twice" is a general description and can also mean that there are two subsequent saves on the same object, and since self.data is reassigned, this leads to data corruption. (thanks, #mudasobwa)
It depends on your model's database field type.
If the field is string type (like VARCHAR or TEXT) it should be stored as string (no need to get rid of the quotes - they are fine). Make sure calling to_json once.
If the field is Postgres JSON type, then you can just assign a hash to the model's field, no need to call to_json at all.
If you are saving hash as a JSON string in a varchar column you can use serialize to handle marshalling/unmarshaling the data:
class Thing < ActiveRecord::Base
serialize :foo, JSON
end
Knowing exactly when to convert the data in the lifecycle of a record is actually quite a bit harder than your naive implementation. So don't reinvent the wheel.
However a huge drawback is that the data cannot be queried in the DB*. If you are using Postgres or MySQL you can instead use a JSON or JSONB (postgres only) column type which allows querying. This example is from the Rails guide docs:
# db/migrate/20131220144913_create_events.rb
create_table :events do |t|
t.json 'payload'
end
# app/models/event.rb
class Event < ApplicationRecord
end
# Usage
Event.create(payload: { kind: "user_renamed", change: ["jack", "john"]})
event = Event.first
event.payload # => {"kind"=>"user_renamed", "change"=>["jack", "john"]}
## Query based on JSON document
# The -> operator returns the original JSON type (which might be an object), whereas ->> returns text
Event.where("payload->>'kind' = ?", "user_renamed")
use {}.as_json instead of {}.to_json
ex:
a = {}
a.as_json # => {}
a.to_json # => "{}"
http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json
I want to store json data which is stored at some url. How can that be done?
Like the comment above, I would recommend a gem like httparty. You can send a get request to the desired url, like response = HTTParty.get('www.exampleurl.com') and use the rails json parsing gem/feature to parse it and create a record from that. But to sum things up:
include 'httparty'
response = HTTParty.get('www.exampleurl.com')
json_response = JSON.parse(response)
json_response.each do |json|
Object.create(name: json.name, some_field: json.some_field)
end
I am having problems accessing the attributes of my JSON data. Instead of accessing the JSON data it thinks it is a function.
#response = HTTParty.get('http://localhost:4000/test')
#json = JSON.parse(#response.body)
#json.each do |pet|
MyModel.create(pet) ! WORKS
puts "my object #{pet}" ! WORKS
puts "my object attribute #{pet.myattribute}" ! DOES NOT WORK
end
With no MethodError myattribute.
Thank you for any help!
You may be used to JavaScript, where both object.some_key and object["some_key"] do the same thing. In Ruby, a hash is just a hash, so you have to access values via object["some_key"]. A Struct in Ruby is similar to a JavaScript object, in that you can access values both ways, but the keys have to be pre-defined.
#json = JSON.parse(#response.body) returns a hash, so you would need to do
puts "my object attributes #{pet['id']}, #{pet['title']}"
you might want to convert to HashWithIndifferentAccess so you can use symbols instead of quoted strings, i.e.
#json = JSON.parse(#response.body).with_indifferent_access
# ...
puts "my object attributes #{pet[:id]}, #{pet[:title]}"
I would like to save query result into redis using JSON serialization and query it back.
Getting query results to json is pretty easy:
JSON.generate(Model.all.collect {|item| item.attributes})
However I did not find a proper way to deserialize it back to ActiveRecord. The most straight-forward way:
JSON.parse(#json_string).collect {|item| Model.new.from_json(item)}
Gives me an error:
WARNING: Can't mass-assign protected attributes: id
So id gets empty. I thought of just using OpenStruct for the views instead of ActiveRecord but I am sure there is a better way.
You could instantiate the new object from JSON and then assign the id afterwards. Probably best to create your own method for this:
class Model
def self.from_json_with_id(params = {})
params = JSON.parse(params)
model = new(params.reject {|k,v| k == "id"})
model.id = params["id"]
model
end
end
Or maybe just override the from_json() method.
Why not like this:
JSON.parse(#json_string).each do |item|
item.delete(:id) # I tested it in my case it also works without this line
object=Model.create(item)
end
If the host that created the JSON adds a JSON root you might have to use item[1] instead of item.
How can I read everything inside a JSON document and create another one with new names?
I can't find something that will help me create a new JSON file in an easy way.
Edit:
I am retrieving a ton of data in JSON format from a MongoDB database (as an array [{"xxx": "zz"}, ... ]). What I need is to cycle trough each document, each field and create a new JSON document using those fields.
thanks
Here's the gist of what you want:
#keys = {
"old" => "new",
"foo" => "bar"
}
def rename_key(pair)
old_key = pair.keys.first
{ #keys[old_key] => pair[old_key] }
end
pairs = ActiveSupport::JSON.decode(json)
pairs.map! { |pair| rename_key(pair) }
new_json = pairs.to_json
Obviously, you'd want to turn this into a class or two. Note that I made the assumption that all data from Mongo was in the form of simple key => value pairs, based on your description.