Failing to parse JSON in Rails - ruby-on-rails

I am trying to have one of my pages request and display info from the last.fm API. At this point there is no user input and I just want the root page on my application to display the results of this API call.
My last_fm Controller:
class LastFmController < ApplicationController
include HTTParty
format :json
base_uri 'http://ws.audioscrobbler.com/2.0/'
def self.get_events
return get('http://ws.audioscrobbler.com/2.0/', :query => {
:method => 'geo.getEvents',
:api_key => 'xxxxxyyyyyyzzzzz'})
end
def index
#events = LastFmController.get_events
end
end
My last_fm#index view is empty except this: <%= #events %>.
The error I'm getting when I go to the last_fm#index view:
795: unexpected token at '<?xml version="1.0" encoding="utf-8"?>
<lfm status="ok">
<events xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" location="San Francisco, United States" page="1" perPage="10" totalPages="37" total="364" festivalsonly="0" tag="">
<event xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" >
<id>3782989</id>
<title>Broken Hope</title>
<artists>
<artist>Broken Hope</artist>
<artist>Oceano</artist>
This trace actually includes the entire hash I received from the API call, so it's long and I'm omitting it for brevity.
The title of the error page is Action Controller: Exception caught.
I would be thankful if I could just do some basic manipulation with the hash I received, like being able to navigate the hash and only display certain items. Thanks!

You are trying to parse XML as JSON. XML parser here: Nokogiri

LastFM returns XML by default. You have to pass in format=json as a paramter so it returns JSON. This should work:
class LastFmController < ApplicationController
include HTTParty
format :json
base_uri 'http://ws.audioscrobbler.com/2.0/'
def self.get_events
return get('http://ws.audioscrobbler.com/2.0/', :query => {
:method => 'geo.getEvents',
:format => 'json',
:api_key => 'xxxxyyyyyzzzz'})
end
def index
#events = LastFmController.get_events
end
end

Related

Rails/Rspec JSON integers being converted to strings when testing post call

I am testing a JSON request to our API, It will respond with JSON.
It seems like all the integers within the JSON get converted to strings as we post them to the controller consider action.
Controller
def consider
binding.pry # binding no# 2 used to check the params after post from test.
if ParametersValidator.is_valid?(params)
application_handler = ApplicationHandler.new(request_interactor)
render json: application_handler.result
else
render json: ParametersValidator.failed_params(params).to_json
end
end
The ParamaterValidator validates the structure and types of data coming in.
Test
render_views
let(:json) { JSON.parse(response.body) }
..
..
it 'returns the result in the correct format for the AUTOMATIC APPROVE decision' do
automatic_approve_params = relative_json_file(relative_file('automatic_approve_params'))
expected_approve_params = {
"status" => "accepted",
"automated" => true,
"rate" => 6,
"amount" => 30000,
"term" => 10,
"pre_approved_amount" => 2500,
"comments" => ""
}
#request.headers['HTTP_X_AUTH_SIG'] = Rails.application.secrets['authorization']['token']
request.env["HTTP_ACCEPT"] = 'application/json'
binding.pry # binding no# 1 to inspect the params before post
post :consider, automatic_approve_params, format: :json
expect(json).to eq(expected_approve_params)
end
Binding no#1
{
"student_id"=>1,
"age"=>22,
"name"=>"John",
"age_range"=>"22-25",
"criminal_record"=>false,
"declared_bankrupt"=>false,
"declared_insolvent"=>false,
"declared_sequestrated"=>false,
"defaulted_on_loan"=>false,
"post_study_salary"=>100000000,
"first_nationality"=>"PL",
"second_nationality"=>"",
"citizenship"=>"PL",
}
Binding no#2
{
"student_id"=>"1",
"age"=>"22",
"name"=>"John",
"age_range"=>"22-25",
"criminal_record"=>false,
"declared_bankrupt"=>false,
"declared_insolvent"=>false,
"declared_sequestrated"=>false,
"defaulted_on_loan"=>false,
"post_study_salary"=>"100000000",
"first_nationality"=>"PL",
"second_nationality"=>"",
"citizenship"=>"PL",
}
The test log is showing that the request is
Processing by Api::V1::CreditApplicationsController#consider as JSON
Inspecting the request just before the post action you will see the params are fine, then in the controller before I run anything I inspect the params and they are all strings.
Using postman to test the API with the JSON works as expected but it seems that rspec when posting to the consider action will convert all the params to strings. I have read a few dozen posts that claim by adding format: :json to the post action it will remedy this, however I have had no such luck.
I am obviously doing something wrong but I have tried pretty much everything I know.
After replicating the issue you are having I managed to resolve it in a controller spec using the following:
post :consider, automatic_approve_params.merge(format: :json)
In my local tests I removed the
request.env["HTTP_ACCEPT"] = 'application/json' and it still worked as you expect it to. Hope it helps.
In Rails 5, use as: :json instead of format: :json, e.g. post :consider, params: automatic_approve_params, as: :json
We can try this
post 'orders.json', JSON.dump(order: {boolean: true, integer: 123}), "CONTENT_TYPE" => "application/json"

Parsing Data from httpparty response

I am returning quite a lot of JSON from a http party request
In my Rails controller I got this data:
#data = #request.parsed_response["InfoResponses"]
and in my html.erb I displayed the #data on screen and it is as below:
[{"AdditionalResponse"=>nil, "AddressResponse"=>nil, "HighResResponse"=>nil, "LowResResponse"=>nil, "OtherResponse"=>nil, "Error"=>nil, "CarResponse"=>{"Values"=>[{"FieldDescription"=>"Colour", "FieldName"=>"COLOUR", "Value"=>BLUE}, {"FieldDescription"=>"Engine Size", "FieldName"=>"ENGINESIZE", "Value"=>1400}, {"FieldDescription"=>"Number Doors", "FieldName"=>"NUMBERDOORS", "Value"=>4}, {"FieldDescription"=>"Fuel", "FieldName"=>"FUEL", "Value"=>GAS}, {"FieldDescription"=>"Wheel Size", "FieldName"=>"WHEELSIZE", "Value"=>17}, {"FieldDescription"=>"CD Player", "FieldName"=>"CDPLAYER", "Value"=>Y}],
"Error"=>nil, "Id"=>"2"}, "Id"=>"2", "DensityResponse"=>nil, "RiskIndexResponse"=>nil, "FinanceResponse"=>{"Value"=>Available, "Error"=>nil, "Id"=>"2"}},
{"AdditionalResponse"=>nil, "AddressResponse"=>nil, "HighResResponse"=>nil, "LowResResponse"=>nil, "OtherResponse"=>nil, "Error"=>nil, "CarResponse"=>{"Values"=>[{"FieldDescription"=>"Colour", "FieldName"=>"COLOUR", "Value"=>RED}, {"FieldDescription"=>"Engine Size", "FieldName"=>"ENGINESIZE", "Value"=>1400}, {"FieldDescription"=>"Number Doors", "FieldName"=>"NUMBERDOORS", "Value"=>4}, {"FieldDescription"=>"Fuel", "FieldName"=>"FUEL", "Value"=>GAS}, {"FieldDescription"=>"Wheel Size", "FieldName"=>"WHEELSIZE", "Value"=>19}, {"FieldDescription"=>"CD Player", "FieldName"=>"CDPLAYER", "Value"=>Y}],
"Error"=>nil, "Id"=>"1"}, "Id"=>"1", "DensityResponse"=>nil, "RiskIndexResponse"=>nil, "FinanceResponse"=>{"Value"=>Available, "Error"=>nil, "Id"=>"1"}}]
On screen I only want to Display the CarResponse Details - i.e all the Fields such as Colout, EngineSize, etc and their values and the only other details I want to extract are the FinanceResponse the Value and the Id.
What is the best way to extract these details - I have shown 2 here but my JSON response could include the same response as above repeated.
EDIT - My HttpParty code is as below
def self.GetInforFromASPWebAPI
#request_body = [{:Id => '1',
:A => '1',
:B => '1',
:C=> '1'}]
post('localhost:50544/api/MyController/GetInfo', :body => #request_body.to_json,
:headers => {'Content-Type' => 'application/json'})
end
This is in a Helper class - then in my controller I was doing something like the below:
#response = ASPWebAPIClass.GetInforFromASPWebAPI
The contents of the response was even more data so I used what I had done before to strip some of it down to the parsed_response.
You should have a full class representing the API. Your methods would then get what you're interested in, rather than passing an entire response back to the Rails controller forcing the controller to know how to parse the API's response. Put all the API knowledge into the API class in accordance with the Single Responsibility Principle. Your code will be cleaner as a result.
Example:
class CarAPI
include HTTParty
base_uri 'localhost:50544/api/MyController'
# any other initialization stuff
def initialize
#options = {
:body => #request_body.to_json,
:headers => {'Content-Type' => 'application/json'}
}
end
def car_info
response = self.class.post '/getinfo'
# Create a simple Ruby hash with key/value pairs
cars = {}
response['InfoResponses'][0]['CarResponses']['Values'].each do |field|
cars[field['FieldName']] = field['Value']
end
cars
end
end
This is a bit simplified; you'll want to iterate over all the responses (replace the [0] with a loop/map).

Rails and XML requests

In my Rails 4 app I am dealing with an API that only handles XML (yes I wish it was JSON too).
I have to make a POST request and the XML string should be stored in a parameter called xmlRequestString.
The XML structure for the POST data is:
<?xml version="1.0" encoding="UTF-8"?>
<GetProperties>
<Auth>
<VendorId>UserID</ VendorId>
<VendorPassword>Password</VendorPassword>
</Auth>
</GetProperties>
As I have never even touched XML before could someone show me how I would actually post this data.
Would something like this be a good way of going about it (borrowed from here: Submitting POST data from the controller in rails to another website)?
require "uri"
require "net/http"
xml = 'xml string can go here'
params = {'xmlRequestString' => xml}
Net::HTTP.post_form(URI.parse('urlendpoint'),params)
You can save this as a template, with instance variables like in a regular html.erb template. Or, you could have it as a method in a model. Either way, you're using something that takes some dynamic data and returns you a text string, that has the xml in it. Then, in your controller, render out the template, or call the method (if you put it in a model) and post it to the api.
#Template method of generating xml
#app/views/properties/get_properties.rxml
xml.instruct! :xml, :version=>"1.0", :encoding => "UTF-8"
xml.GetProperties do
xml.Auth do
xml.VendorId do
<%= #user_id %>
end
xml.VendorPassword do
<%= #password %>
end
end
end
Then, in a controller, call the api:
#user_id = "foo"
#password = "bar"
xml_string = render :template => "properties/get_properties.rxml", :layout => false
http = Net::HTTP.new("www.the-api-website.com")
response = http.post("/path/to/call/the/api", xml_string)

Add generic fields to rails JSON response

I need to render JSON response for the REST api in which I need to include additional JSON fields that are not part of the model being rendered. I did read this link about ActiveModel where it suggests to use the ":methods" option to call another method where I can additional generic fields.
def add_fields
{ "field1" => "true" }
end
if #user.save
render :json => #user.as_json(:only => [:username, :org], :methods => [:add_fields])
endif
However, when the JSON response is received only the username & org fields are returned in the JSON. The additional parameters defined in the method "add_fields" are not added. What is it that I am missing due to which additional fields are not being added to JSON response. Or is there a better way to add generic fields (not part of model) in JSON response?
May be you forgot to implement the #add_fields method in User.
I just tested this on my own User model and it worked just fine.
2.0.0p195 :002 > user.as_json(only: [:first_name, :last_name], methods: [:full_name])
{
"first_name" => "Cody",
"last_name" => "Russell",
:full_name => "Cody Russell"
}
Are you sure you have the method defined, and it's a public method?

How to serve a static JSON file in rails?

I have a file on my server that's outside of my app directory. It's a text file that holds a json object, /path/to/data.json.
What's the simplest way to serve this file in Rails, e.g. to return it in response to a GET request?
Here's what I've tried so far, inspired by this answer and others. (This may be way off-base -- I'm new to rails.)
added this line to routes.rb
resources :data
Wrote the following data_controller.rb
class DataController < ApplicationController
#data = File.read("/path/to/data.json")
def index
render :json => #data
end
end
This doesn't work. When I direct my browser to http://myserver.com/data.json I just see "null" instead of the data.json file.
Any idea what I'm doing wrong?
I think this is a scope issue; your outer #data is not the same as the #data in a method. That is, you can't use instance variables as expected outside of methods because there is no instance yet.
It should work if you use a class variable, eg
##data = File.read("/path/to/data.json")
then
render :json => ##data
Put it in public/assets/javascripts. Or app/assets/javascripts. The server may even return the correct content-type.
put data.json file into your project directory ( for example public/data.json)
#data = File.read("#{Rails.root}/public/data.json")
At last but not least render :json => #data
You can also do it in one line, e.g.
render :file => '/path/to/data.json',
:content_type => 'application/json',
:layout => false

Resources