Why key of hash is not parsed - ruby-on-rails

I'm working with hash like this, the first key is a hash
hash = { { a: 'a', b: 'b' } => { c: { d: 'd', e: 'e' } } }
when I convert it to json, I get this:
data_json = hash.to_json
# => "{\"{:a=\\u003e\\\"a\\\", :b=\\u003e\\\"b\\\"}\":{\"c\":{\"d\":\"d\",\"e\":\"e\"}}}"
But when I parse the data, the first key is not parsed:
JSON.parse data_json
# => {"{:a=>\"a\", :b=>\"b\"}"=>{"c"=>{"d"=>"d", "e"=>"e"}}}
Why JSON.parse acts like that? and How can I fix it ?

In your original data structure, you have a Hash containing a single key-value pair. However, the key for this pair is itself a hash. This is not allowed in JSON (as only string keys are allowed in JSON), resulting in Ruby's JSON library to trying to do something sensible here: it inspects the key and uses the resulting String as the key in the created JSON object.
Unfortunately, this operation is not reversible when parsing the JSON object again. To solve this, You should try to adapt your source data structure to match what is allowed in JSON, i.e. to only use String keys in your hashes.

Related

Unable to decode json string from Dictionary object in same order and sequence in Swift iOS

I have created a Dictionary with some values. I want to create JSON string of that dictionary object.
I am using String(data: data, encoding: .utf8)! to create json string but every time i run this code i am getting json string in diffrent sequence and order of same data.
import Foundation
import CommonCrypto
var object = ["emp1":["name":"neeraj","age":"14","degree":"Btech"],
"emp2":["name":"ajay","age":"24","degree":"Mca"],
"emp3":["name":"vijay","age":"34","degree":"Bca"],
"emp4":["name":"raju","age":"44","degree":"Mtech"]]
if let data = try? JSONSerialization.data(withJSONObject: object, options: []){
print(String(data: data, encoding: .utf8)!)
}
First time result:
{"emp2":{"age":"24","degree":"Mca","name":"ajay"},"emp4":{"age":"44","name":"raju","degree":"Mtech"},"emp3":{"degree":"Bca","name":"vijay","age":"34"},"emp1":{"degree":"Btech","name":"neeraj","age":"14"}}
Second time result:
{"emp1":{"age":"14","degree":"Btech","name":"neeraj"},"emp4":{"name":"raju","degree":"Mtech","age":"44"},"emp3":{"name":"vijay","age":"34","degree":"Bca"},"emp2":{"name":"ajay","age":"24","degree":"Mca"}}
I want same result for every time.
I need same order to create md5 checksum to compare with recieved checksome of data.
I am getting a response from web service
for e.g
{
"payload":{
"object1":["name":"neeraj"],
"object2":["name":"ajay"]
},
"hash":"<hash of payload using md5>"
}
i have to create an md5 hash at my side and need to verify with hash i recieved.
but when i create JSON String i got different order and my hash doesn't match with recieved hash.
Please help
Don't use MD5 it's broken.
Create your dictionary from the JSON parse and then instantiate objects from that dict.
Hash the objects with a sensible digest such as Keccak, SHA-2, SHA-3 or Blake2.

Extracting hash key that may or may not be an array

I'm making an API call that returns XML (JSON also available) and the response body will show errors if any. There may be only one error or multiple errors. When the XML (or JSON) is parsed into a hash, the key that holds the errors will be an array when multiple errors are present but will be just a standard key when only one error is present. This makes parsing difficult as I can't seem to come up with one line of code that would fit both cases
The call to the API returns this when one error
<?xml version=\"1.0\" encoding=\"utf-8\"?><response><version>1.0</version><code>6</code><message>Data validation failed</message><errors><error><parameter>rptFilterValue1</parameter><message>Parameter is too small</message></error></errors></response>
And this when multiple errors
<?xml version=\"1.0\" encoding=\"utf-8\"?><response><version>1.0</version><code>6</code><message>Data validation failed</message><errors><error><parameter>rptFilterValue1</parameter><message>Parameter is too small</message></error><error><parameter>rptFilterValue2</parameter><message>Missing required parameter</message></error></errors></response>
I use the following to convert the XML to a Hash
Hash.from_xml(response.body).deep_symbolize_keys
This returns the following hash.
When there is only one error, the hash looks like this
{:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}}}}
When there are 2 errors, the hash looks like this
{:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>[{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}, {:parameter=>"rptFilterValue2", :message=>"Missing required parameter"}]}}}
When I first tested the API response, I had multiple errors so the way I went about getting the error message was like this
data = Hash.from_xml(response.body).deep_symbolize_keys
if data[:response].has_key?(:errors)
errors = data[:response][:errors][:error].map{|x| "#{x.values[0]} #{x.values[1]}"}
However when there is only one error, the code errors out with undefined method 'values' for parameter
The only actual workaround I found was to test the class of the error key. When Array I use one method for extracting and when Hash I use another method.
if data[:response][:errors][:error].class == Array
errors = data[:response][:errors][:error].map{|x| "#{x.values[0]} #{x.values[1]}"}
else
errors = data[:response][:errors][:error].map{|x| "#{x[1]}"}
end
But I just hate hate hate it. There has to be a way to extract xml/json data from a key that may or may not be an array. The solution may be in the conversion from xml to hash rather than when parsing the actual hash. I couldn't find anything online.
I'll appreciate any help or tip.
If you're using Rails, Array#wrap is available if you can do your .dig first:
single = {:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}}}}
Array.wrap(single.dig(:response, :errors, :error))
This returns an Array of size 1:
[
{
:message => "Parameter is too small",
:parameter => "rptFilterValue1"
}
]
For multiples:
multiple = {:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>[{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}, {:parameter=>"rptFilterValue2", :message=>"Missing required parameter"}]}}}
Array.wrap(multiple.dig(:response, :errors, :error))
This returns an Array of size 2:
[
{
:message => "Parameter is too small",
:parameter => "rptFilterValue1"
},
{
:message => "Missing required parameter",
:parameter => "rptFilterValue2"
}
]
You can parse XML with Nokogiri and xpath, which returns array even if selector points out single element
errors = Nokogiri::XML(xml_response).xpath('//error')
errors.map { |e| e.children.each_with_object({}) { |x, h| h[x.name] = x.content } }
Your API response with single error gives
=> [{"parameter"=>"rptFilterValue1", "message"=>"Parameter is too small"}]
and API result with multiple errors
=> [{"parameter"=>"rptFilterValue1", "message"=>"Parameter is too small"}, {"parameter"=>"rptFilterValue2", "message"=>"Missing required parameter"}]
If there's no error elements you'll get an empty array.

how to store json to database in rails

have URl want to store json data to database automatically
site provide this code but use Java
HttpResponse response = Unirest.get("https://xproject.restdb.io/rest/data")
.header("x-apikey", "42ba8a6dc7532aa8319a58310281665e394e4")
.header("cache-control", "no-cache")
.asString();
This Example of json
model called speed contain speed & id
need save gps_speed_kph = speed & id_watch ==_id
Firstly, you have to specify the db column data type as hash in your model as described here.
Then you have to save your json as hash in your db (json to hash ):
require 'json'
value = '{"val":"test","val1":"test1","val2":"test2"}'
myhash = JSON.parse(value)
Model.create(hash_column_name: myhash)
and for converting you can do this (hash to json):
#myhash = Model.find(1).hash_column_name
json_data = #myhash.to_json
You can use JSONB column. This may help you to update the single are the group of data based on the key in the column.
Ref:
1) http://edgeguides.rubyonrails.org/active_record_postgresql.html#json-and-jsonb
2) https://nandovieira.com/using-postgresql-and-jsonb-with-ruby-on-rails

Pass array of ids to custom get in Restangular

I'm trying to pass an array of ids to my server using Restangular
var params = { profile_ids: _.map(fof, 'id') }
Restangular.all('mutual_friends').customGET('', params)
however, this results in the following query string being passed the my rails server
Started GET "/mutual_friends?profile_ids=231&profile_ids=2&profile_ids=254
how can I send the ids as an array? so the query string looks like:
/mutual_friends?profile_ids=[231, 2, 354]

How to accept multiple URL parameters in Rails with the same key?

In Rails, when you want to send a list of values through a single array parameter, you usually do so by suffixing the URL parameter key with []. For example, the query string ?foo[]=1&foo[]=2&foo[]=3 is parsed as
params = { "foo" => [ 1, 2, 3 ] }
But for ?foo=1&foo=2&foo=3, only the last argument shows up in the params hash:
params = { "foo" => 3 }
Everything's fine while you can control the URL format; you can just use the [] syntax. But what if the URL is constructed by a remote system that you cannot influence, and that insists on the second format? How can one get the arguments unpacked properly?
Using the hint from #maxcal I came up with this solution (to avoid adding yet more gems to this completely bloated app that I'm working on):
current_query_string = URI(request.url).query
foo_values = URI::decode_www_form(current_query_string).
select { |pair| pair[0] == "foo" }.
collect { |pair| pair[1] }

Resources