JSON to Hash in Ruby and vice-versa using Files - Parser Error - json

I am trying to save data from a Hash to a file. I convert it to JSON and dump it into the file.
When I try to parse back from file to hash I get JSON::ParserError
Code to convert Hash to JSON file: (works fine)
user = {:email => "cumber#cc.cc", :passwrd => "hardPASSw0r|)"}
student_file = File.open("students.txt", "a+") do |f|
f.write JSON.dump(user)
end
After adding a few values one by one to the file it looks something like this:
{"email":"test1#gmail.com","passwrd":"qwert123"}{"email":"test3#gmail.com","passwrd":"qwert12345"}{"email":"cumber#cc.cc","passwrd":"hardPASSw0r|)"}
I tried the following code to convert back to Hash but it doesn't work:
file = File.read('students.txt')
data_hash = JSON.parse(file)
I get
System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/json/common.rb:155:in `parse': 757: unexpected token at '{"email":"test3#gmail.com","passwrd":"qwert12345"}{"email":"cumber#cc.cc","passwrd":"hardPASSw0r|)"}' (JSON::ParserError)
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/json/common.rb:155:in `parse'
from hash_json.rb:25:in `<main>'
My goal is to be able to add and remove values from the file.
How do I fix this, where was my mistake? Thank you.

This should work:
https://repl.it/EXGl/0
# as adviced by #EricDuminil, on some envs you need to include 'json' too
require 'json'
user = {:email => "cumber#cc.cc", :passwrd => "hardPASSw0r|)"}
student_file = File.open("students.txt", "w") do |f|
f.write(user.to_json)
end
file = File.read('students.txt')
puts "saved content is: #{JSON.parse(file)}"
p.s. hope that this is only an example, never store passwords in plain-text! NEVER ;-)

Related

Can't read JSON file in Ruby on Rails

I am new in ruby on rails and I want to read data from a JSON file from a specified directory, but I constantly get an error in chap3(File name)
Errno::ENOENT in TopController#chap3. No such file or directory # rb_sysopen - links.json.
In the console, I get a message
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
How I can fix that?
Code:
require "json"
class TopController < ApplicationController
def index
#message = "おはようございます!"
end
def chap3
data = File.read('links.json')
datahash = JSON.parse(data)
puts datahash.keys
end
def getName
render plain: "名前は、#{params[:name]}"
end
def database
#members = Member.all
end
end
JSON file:
{ "data": [
{"link1": "http://localhost:3000/chap3/a.html"},
{"link2": "http://localhost:3000/chap3/b.html"},
{"link3": "http://localhost:3000/chap3/c.html"},
{"link4": "http://localhost:3000/chap3/d.html"},
{"link5": "http://localhost:3000/chap3/e.html"},
{"link6": "http://localhost:3000/chap3/f.html"},
{"link7": "http://localhost:3000/chap3/g.html"}]}
I would change these two lines
data = File.read('links.json')
datahash = JSON.parse(data)
in the controller to
datahash = Rails.root.join('app/controllers/links.json').read
Note: I would consider moving this kind of configuration file into the /config folder and creating a simple Ruby class to handle it. Additionally, you might want to consider paths instead of URLs with a host because localhost:3000 might work in the development environment but in production, you will need to return non-localhost URLs anyway.
Rails use the content of file in the controller
#data = File.read("#{Rails.root}/app/controllers/links.json")

Writing to a .json file

I am currently trying to write 2 pieces of user inputted code to a .json file without clearing the existing data. I believe it is a problem with the logins.append as it says there is no such thing. What would i have to use?
I have been searching around trying to find different suffixes to the logins.
def i():
path_to_json = "./logins.json"
with open("logins.json", "r") as content:
logins = json.load(content)
with open('logins.json', 'a') as outfile:
username = str(input('New Username: '))
password = str(input('New Password: '))
logins.append({username:password})
I am getting the error:
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
i()
File "N:\NEA Computing\NEA code.py", line 188, in i
logins.append({username: password})
AttributeError: 'dict' object has no attribute 'append'
I am expecting it to add data to the .json file without deleting the other data however i am getting an error and nothing is being written to the .json file.
Try to open the file with 'w' option
def i():
path_to_json = "./logins.json"
with open("logins.json", "w") as content:
logins = json.load(content)
with open('logins.json', 'a') as outfile:
username = str(input('New Username: '))
password = str(input('New Password: '))
logins.append({username:password})
Maybe this is a misunderstanding of the question, but what is happening, as far as I can see, is that you use json.load, which seems to load an object literal like {key1: value1, key2: value2,...} and not an array literal like [value1, value2, ...]. Python converts the first one into a python dict, and a dictionary has no append function, only an array / a list has. You get your error and execution halts. When you have the content of logins.json as object {...}, it doesn't work for me but as array/list [...] it works

Trouble parsing file obtained from Dir.glob

I am new to ruby. Running into error while trying to parse file obtained from Dir.glob command using JSON.parse()
require 'json'
Dir.glob('**/*/.json').each do |f| # find all the .json file and loop each file
puts f
data = JSON.parse(f)
if data['Apple'].nil?
puts "skipping file #{f} as it does not have Apple"
next
end
parsed_key= File.dirname(data['Apple'][0]['red'][0]['key'])
puts parsed_key
end
`parse': 767: unexpected token at 'xyz/abc/config.json' (JSON::ParserError)
f there is just the path, a String. If you want the contents of the file you'll need to read it first, data = JSON.parse(File.read(f))

Ruby: Handling different JSON response that is not what is expected

Searched online and read through the documents, but have not been able to find an answer. I am fairly new and part of learning Ruby I wanted to make the script below.
The Script essentially does a Carrier Lookup on a list of numbers that are provided through a CSV file. The CSV file has just one row with the column header "number".
Everything runs fine UNTIL the API gives me an output that is different from the others. In this example, it tells me that one of the numbers in my file is not a valid US number. This then causes my script to stop running.
I am looking to see if there is a way to either ignore it (I read about Begin and End, but was not able to get it to work) or ideally either create a separate file with those errors or just put the data into the main file.
Any help would be much appreciated. Thank you.
Ruby Code:
require 'csv'
require 'uri'
require 'net/http'
require 'json'
number = 0
CSV.foreach('data1.csv',headers: true) do |row|
number = row['number'].to_i
uri = URI("https://api.message360.com/api/v3/carrier/lookup.json?PhoneNumber=#{number}")
req = Net::HTTP::Post.new(uri)
req.basic_auth 'XXX' , 'XXX'
res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) {|http|
http.request(req)
}
json = JSON.parse(res.body)
new = json["Message360"]["Carrier"].values
CSV.open("new.csv", "ab") do |csv|
csv << new
end
end
File Data:
number
5556667777
9998887777
Good Response example in JSON:
{"Message360"=>{"ResponseStatus"=>1, "Carrier"=>{"ApiVersion"=>"3", "CarrierSid"=>"XXX", "AccountSid"=>"XXX", "PhoneNumber"=>"+19495554444", "Network"=>"Cellco Partnership dba Verizon Wireless - CA", "Wireless"=>"true", "ZipCode"=>"92604", "City"=>"Irvine", "Price"=>0.0003, "Status"=>"success", "DateCreated"=>"2018-05-15 23:05:15"}}}
The response that causes Script to stop:
{
"Message360": {
"ResponseStatus": 0,
"Errors": {
"Error": [
{
"Code": "ER-M360-CAR-111",
"Message": "Allowed Only Valid E164 North American Numbers.",
"MoreInfo": []
}
]
}
}
}
It would appear you can just check json["Message360"]["ResponseStatus"] first for a 0 or 1 to indicate failure or success.
I'd probably add a rescue to help catch any other errors (malformed JSON, network issue, etc.)
CSV.foreach('data1.csv',headers: true) do |row|
number = row['number'].to_i
...
json = JSON.parse(res.body)
if json["Message360"]["ResponseStatus"] == 1
new = json["Message360"]["Carrier"].values
CSV.open("new.csv", "ab") do |csv|
csv << new
end
else
# handle bad response
end
rescue StandardError => e
# request failed for some reason, log e and the number?
end

export to csv using fastercsv and CSV::Writer (Ruby on Rails)

What am I trying to do: export data to csv.
I have a form which allows user to select the format (from a drop down menu). So based on the selection of the format the ouput is displayed using a ajax call. Works fine for html but when I select the format as csv I don't see any pop up on the screen (asking to save or open the file) and neither any file gets downloaded directly.
I tried using Fastercsv (but the problem is that I don't see any pop up asking me whether I want to save or open the file) and CSV::Writer where I get this error message on the console.
NoMethodError (You have a nil object when you didn't expect it!
The error occurred while evaluating nil.bytesize):
actionpack (2.3.4) lib/action_controller/streaming.rb:142:in `send_data'
Code using Fastercsv:
def export_to_csv
csv_string = FasterCSV.generate(:col_sep => ",") do |csv|
members = ["Versions / Project Members"]
members_selected.each {|member| members << Stat.member_name(member)}
Stat.project_members(project).each {|user| members << user.name}
csv << ["some text", "text 2", "text 3"]
end
return csv_string
end
and this is how I am sending the data:
send_data(export_to_csv,:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment", :filename => "filename.csv")
I see the response as "some text, text 2, text 3" in the firebug console but no pop up asking whether I want to save or open the file.
This is what I am doing using CSV::Writer:
def export_to_csv
report = StringIO.new
CSV::Writer.generate(report, ',') do |csv|
csv << ['c1', 'c2']
end
end
and call it as:
send_data(export_to_csv,:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment", :filename => "filename.csv")
This is the error which is thrown on the console:
NoMethodError (You have a nil object when you didn't expect it!
The error occurred while evaluating nil.bytesize):
actionpack (2.3.4) lib/action_controller/streaming.rb:142:in `send_data'
send_data is trying to reference an object that is out of scope. Check your closing 'end' statement