Destroy JSON primitives with Ruby JSON built-in library

Why doesn't Ruby inline JSON deserialize simple JSON primitives and how do I get around it?

irb(main):001:0> require 'json' #=> true irb(main):002:0> objects = [ {}, [], 42, "", true, nil ] #=> [{}, [], 42, "", true] irb(main):012:0> objects.each do |o| irb(main):013:1* json = o.to_json irb(main):014:1> begin irb(main):015:2* p JSON.parse(json) irb(main):016:2> rescue Exception => e irb(main):017:2> puts "Error parsing #{json.inspect}: #{e}" irb(main):018:2> end irb(main):019:1> end {} [] Error parsing "42": 706: unexpected token at '42' Error parsing "\"\"": 706: unexpected token at '""' Error parsing "true": 706: unexpected token at 'true' Error parsing "null": 706: unexpected token at 'null' #=> [{}, [], 42, "", true, nil] irb(main):020:0> RUBY_DESCRIPTION #=> "ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-darwin10.7.0]" irb(main):022:0> JSON::VERSION #=> "1.4.2" 
+6
source share
3 answers

RFC 4627: The application type / json Media for JavaScript Object Notation (JSON) has the following:

 2. JSON Grammar A JSON text is a sequence of tokens. The set of tokens includes six structural characters, strings, numbers, and three literal names. A JSON text is a serialized object or array. JSON-text = object / array [...] 2.1. Values A JSON value MUST be an object, array, number, or string, or one of the following three literal names: false null true 

If you call to_json on your six samples, we get the following:

 >> objects = [ {}, [], 42, "", true, nil ] >> objects.map { |o| puts o.to_json } {} [] 42 "" true null 

Thus, the first and second are valid JSON texts, while the last four are invalid JSON texts, even if they are valid JSON values.

JSON.parse wants it to call a JSON document:

Parse the JSON document source into a Ruby data structure and return it.

Perhaps the JSON document is a library term for RFC 4627 to invoke JSON text. If so, then raising the exception is a reasonable response to invalid input.

If you forcefully wrap and expand everything:

 objects.each do |o| json = o.to_json begin json_text = '[' + json + ']' p JSON.parse(json_text)[0] rescue Exception => e puts "Error parsing #{json.inspect}: #{e}" end end 

And as you noticed in your comment, using an array as a wrapper is better than an object if the caller wants to use the parameter :symbolize_names . Such a wrapper means that you will always feed JSON text to JSON.parse , and everything should be fine.

+9
source

It seems that the built-in JSON parser intentionally fails on nothing but objects and arrays. My current workaround is as follows:

 # Work around a flaw in Ruby built-in JSON parser # not accepting anything but an object or array at the root level. module JSON def self.parse_any(str,opts={}) parse("[#{str}]",opts).first end end 
+1
source

I think you're right ... whether it is a mistake or not, there is some elusive logic that happens to the implementation. If he can parse arrays and hashes, he should be able to parse everything else.

Since JSON.parse seems to be intended for objects and arrays, I would try to pass your data in one of these ways, if you can, and if you cannot, stick to the workaround that you have.

-1
source

Source: https://habr.com/ru/post/899883/


All Articles