Passing a hash where the keys are objects to JSON? - ajax

I am trying to pass a hash into Javascript via JSON where the keys are ruby objects, and the values are arrays of objects. The arrays of objects are transmitted just fine, but the key is being converted into a string of the class.
Here is an example:
[4] pry(#<User>)> x = find_all_sections.collect { |s| { s => s.find_all_events_id_and_title } }
=> [{#<Section id: 58, course_id: 12, section_number: 3, semester_id: 1>} =>
[{:id=>37, :title=>"Event 37"},
{:id=>40, :title=>"Event 40"},
{:id=>9, :title=>"Event 9"},
{:id=>10, :title=>"Event 10"},
{:id=>16, :title=>"Event 16"},
{:id=>38, :title=>"Event 38"}, etc...
The result of converting this to json is (you just have to look at the first few characters of the string to see that it's not the object, but the to_s of the object:
[11] pry(#<User>)> x.to_json
=> "[{\"#<Section:0x007fa475b52f68>\":[{\"id\":37,\"title\":\"Event 37\"},{\"id\":40,\"title\":\"Event 40\"},{\"id\":9,\"title\":\"Event 9\"},{\"id\":10,\"title\":\"Event 10\"},{\"id\":16,\"title\":\"Event 16\"},{\"id\":38,\"title\":\"Event 38\"},{\"id\":49,\"title\":\"Event 49\"},{\"id\":39,\"title\":\"Event 39\"},{\"id\":15,\"title\":\"Event 15\"},{\"id\":25,\"title\":\"Event 25\"},{\"id\":11,\"title\":\"Event 11\"},{\"id\":4,\"title\":\"Event 4\"},{\"id\":22,\"title\":\"Event 22\"},{\"id\":1,\"title\":\"Event 1\"},{\"id\":23,\"title\":\"Event 23\"},{\"id\":8,\"title\":\"Event 8\"},{\"id\":13,\"title\":\"Event 13\"},{\"id\":26,\"title\":\"Event 26\"},{\"id\":46,\"title\":\"Event 46\"},{\"id\":20,\"title\":\"Event 20\"},{\"id\":31,\"title\":\"Event 31\"},{\"id\":6,\"title\":\"Event 6\"},{\"id\":18,\"title\":\"Event 18\"},{\"id\":41,\"title\":\"Event 41\"},{\"id\":7,\"title\":\"Event 7\"},{\"id\":43,\"title\":\"Event 43\"},{\"id\":45,\"title\":\"Event 45\"},{\"id\":24,\"title\":\"Event 24\"},{\"id\":2,\"title\":\"Event 2\"},{\"id\":44,\"title\":\"Event 44\"},{\"id\":29,\"title\":\"Event 29\"},{\"id\":28,\"title\":\"Event 28\"},{\"id\":5,\"title\":\"Event 5\"},{\"id\":3,\"title\":\"Event 3\"},{\"id\":27,\"title\":\"Event 27\"}]},
How can I achieve the JSON data keeping the key's object intact?

You can't. Keys are always strings in JavaScript. Furthermore, the JSON spec says that keys must always have the form of a single, double-quoted string literal.

Related

How to convert Ruby protobuf Message to JSON while preserving the case used in the proto?

When you convert a Protobuf Message in Ruby to JSON using to_json it converts all fieldnames to camelCase.
e.g. with protobuf message Person as
message Person {
string name = 1;
int32 id = 2;
string email_address = 3;
and Person in Ruby as
person = Person.new(:name => "Bob",
:id => 1,
:email_address => "foo#bar.com")
Serialized to JSON
person.to_json
>>> {"name":"Bob","id":"1","emailAddress":"foo#bar.com"}
the field email_address gets serialized in camelCase instead of snake case as it is in the proto
How can you serialize it with the original proto fieldnames?
I tried converting it to a Ruby Hash (with .to_h) at first since it preserves field names, but ran into a different issue. Fields with double values will be rendered as a Hash like price: {"value": 10.0"} instead of price: 10.0.
Buried deep in the source code is the answer.
There is an option in to_json to preserve the case used in the proto by passing in preserve_proto_fieldnames: true
e.g. person.to_json({preserve_proto_fieldnames: true})
Unfortunately this doesn't seem to be elsewhere in the Ruby protobuf documentation

Pulling out values from element of array

I have a hash that is generated by IB-ruby and looks like this:
{:contract=>#<IB::Stock:0x0000560721a1aee0 #attributes={:symbol=>"AAPL", :currency=>"USD", :sec_type=>"STK", :created_at=>2019-10-23 23:03:35 +0200, :con_id=>0, :right=>"", :include_expired=>false, :exchange=>"SMART"}>, :last_price=>0.24308e3, :high=>0.24324e3, :low=>0.24122e3, :close_price=>0.23996e3, :open_tick=>0.2421e3, :bid_price=>0.2431e3, :ask_price=>0.24319e3}
How do I pull out the symbol ("AAPL") and the closing_price (0.23996e3) for further processing?
What you posted is the string representation of a Hash.
This Hash has a key :close_price, whose value you can access in this way:
your_hash[:close_price] #=> 0.23996e3
The hash also has a key :contract whose value is an instance of the class IB::Stock. To access this object:
ib_stock_instance = your_hash[:contract]
ib_stock_instance.class #=> IB::Stock

Hyphens in serialized JSON OpenStruct

I have a JSON object such as:
"c": {
"10-20": 9.0,
"0-10": 8.5,
"30-end": 5.085714285714286,
"20-30": 10.3
}
When I convert that JSON to a serialized object using:
JSON.parse(response.body, object_class: OpenStruct)
It gives me:
<OpenStruct 10-20=0, 0-10=8.5, 30-end=5.085714285714286, 20-30=10.3>
Naturally that can't be accessed with c.10-20 as I don't believe hyphens are valid class variable names. So, how do you access these values?
You can use square brackets like you would with a hash:
obj["10-20"]
#=> 0
Of course, if most of the keys are not valid method names anyway, then you might as well just use a hash and not bother with an OpenStruct.
Related documentation: OpenStruct#[]

Ruby to_yaml stringifies my json

I am trying to convert a ruby hash to yaml. I'd like part of the hash be valid json; however, when I try to serialize the json string, it is converted to yaml in quotes.
For example, when I just have a simple string, the ouput is as follows (note foo is not in quotations):
request = {}
request['body'] = 'foo'
request.to_yaml # outputs: body: foo
However, when I add something to the beginning of the string, like { foo the output for body gets quoted:
request['body'] = '{ foo'
request.to_yaml # outputs: body: '{ foo'
How can I get around this? I've tried JSON.parse and, though that make work, I can't be guaranteed that this input will actually be json (could be xml, etc...) -- I just want to give back whatever was given to me but not "stringified".
Basically, I want to give an object that looks like:
{ 'request' => {
'url' => '/posts',
'method' => 'GET',
'headers' => [
'Content-Type' => 'application/json'
]
},
'response' => {
'code' => 200,
'body' => '[{"id":"ef4b3a","title":"this is the title"},{"id":"a98c4f","title":"title of the second post"}]'
}
}
Which returns:
request:
url: /posts
method: GET
headers:
- Content-Type: application/json
response:
code: 200
body:
[{"id":"ef4b3a","title":"this is the title"},{"id":"a98c4f","title":"title of the second post"}]
The reason being: right now, I can go from yaml to the correct ruby hash but I can't go the other way.
The method my_hash.to_yaml() just takes a hash and converts it to YAML without doing anything special to the values. The method does not care whether your string is JSON or XML, it just treats it as a string.
So why is my JSON being put into quotes when other strings aren't?
Good question! The reason is simple: curly braces are a valid part of YAML syntax.
This:
my_key: { sub: 1, keys: 2}
Is called flow mapping syntax in YAML, and it allows you make nested mappings in one line. To escape strings which have curly braces in them, YAML uses quotes:
my_key: "{ sub: 1, keys: 2}" # this is just a string
Of course, the quotes are optional for all strings:
my_key: "foo" #same as my_key: foo
Okay, but I want to_yaml() to find my JSON string and convert it to YAML mappings like the rest of the hash.
Well then, you need to convert your JSON string to a hash like the rest of your hash. to_yaml() converts a hash to YAML. It doesn't convert strings to YAML. The proper method for doing this is to use JSON.parse, as you mentioned:
request['body'] = JSON.parse( '{"id":"ef4b3a"}' )
But the string might not be JSON! It might be XML or some other smelly string.
This is exactly why to_yaml() doesn't convert strings. A wise programmer once told me: "Strings are strings. Strings are not data structures. Strings are strings."
If you want to convert a string into a data structure, you need to validate it and parse it. Because there's no guarantee that a string will be valid, it's your responsibility as a programmer to determine whether your data is JSON or XML or just bad, and to decide how you want to respond to each bit of data.
Since it looks like you're parsing web pages, you might want to consider using the same bit of data other web clients use to parse these things:
{ 'request' => {
'url' => '/posts',
'method' => 'GET',
'headers' => [
'Content-Type' => 'application/json' #<== this guy, right here!
]
},
'response' => {
'code' => 200,
'body' => '[{"id":"ef4b3a","title":"this is the title"},{"id":"a98c4f","title":"title of the second post"}]'
}
}
If the content-type doesn't agree with the body then you should throw an error because your input data is bad.
The reason '{ foo' requires quote is because this is part of the YAML specification 7.3.3 Plain Style.
Excerpt
Plain scalars must never contain the “: ” and “#” character combinations. Such combinations would cause ambiguity with mapping key: value pairs and comments. In addition, inside flow collections, or when used as implicit keys, plain scalars must
not contain the “[”, “]”, “{”, “}” and “,” characters. These characters would cause ambiguity with flow collection structures.
Based on the above even your stated "return" value is incorrect and the body is probably enclosed in single quotes e.g.
response:
code: 200
body: '[{"id":"ef4b3a","title":"this is the title"},{"id":"a98c4f","title":"title of the second post"}]'
Otherwise it would create ambiguity with "Flow Sequences" ([,]) and "Flow Mappings" ({,}).
If you would like result of the JSON, XML or other notation language to be represented appropriately (read objectively) then you will need to determine the correct parser (may be from the "Content-Type") and parse it before converting it YAML

Mapping a JSON array of unnamed values with RESTKIT 0.20

When I configure mapping from a REST service returning JSON to a object, I normally do this:
RKObjectMapping *myMapping = [RKObjectMapping mappingForClass:[MyClass class]];
[myMapping addAttributeMappingsFromDictionary:#{#"Address" : #"address", #"City" : #"city"}];
and this works great for JSON with named attributes, but how do I map the following JSON to a object with the property "name"?
["My Value","Some other value","More stuff","Hello World"]
This JSON is just a array of values and has not name/key only values. How do I map this to a object with RESTKIT 0.20?
Thank you
Søren
This expression in square brackets is a json array: http://www.json.org. If you look on the syntax tree on the home page, you can consider, every json array is a value of a "variable" with a name. It means your expression has to look like this, to be a valid json:
{ "myArray": ["My Value","Some other value","More stuff","Hello World"] }
and you map it like you always do:
[myMapping addAttributeMappingsFromDictionary:#{#"myArray" : #"myArray"}];
Your parameter MyArray in mapping target class has then a type of NSArray.

Resources