使用内置的Ruby JSON库反序列化JSON基元

为什么Ruby的内置JSON不能反序列化简单的JSON原语,我该如何解决它?

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" 

RFC 4627:JavaScript Object Notation(JSON)的application / json媒体类型有这样的说法:

 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 

如果你在六个样本对象上调用to_json ,我们得到:

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

所以第一个和第二个是有效的JSON文本,而后四个不是有效的JSON文本,即使它们是有效的JSON值

JSON.parse想要它所谓的JSON文档:

将JSON文档解析为Ruby数据结构并将其返回。

也许JSON文档是RFC 4627称为JSON文本的库的术语。 如果是这样,那么引发exception是对无效输入的合理响应。

如果你强行包装和打开所有东西:

 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 

正如您在注释中所指出的,在调用者想要使用:symbolize_names选项的情况下,使用数组作为包装器比对象更好。 像这样包装意味着你将永远为JSON.parse一个JSON文本,一切都应该没问题。

看起来内置的JSON解析器故意在除了对象和数组之外的任何东西上失败。 我目前的解决方法如下:

 # Work around a flaw in Ruby's 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 

这是一个相当古老的问题,但我认为有必要找到一个正确的答案,以防止那些刚刚遇到问题但仍在寻找解决方案的人脱发:)

为了能够使用版本2以下的 JSON gem解析“JSON基元”,您可以像这样传递quirks_mode: true选项;

 JSON::VERSION # => 1.8.6 json_text = "This is a json primitive".to_json JSON.parse(json_text, quirks_mode: true) 

如果JSON gem版本大于或等于2 ,则不再需要quirks_mode。

 JSON::VERSION # => 2.0.0 json_text = "This is a json primitive".to_json JSON.parse(json_text) 

在解析JSON之前,您可以使用bundle show jsongem list | grep json检查项目中使用的JSON gem的版本。 gem list | grep json然后使用相应的一个。

快乐的JSON解析!

我认为你是对的……无论是否是一个bug,实现都会有一些不稳定的逻辑。 如果它可以解析数组和散列,它应该能够解析其他所有内容。

因为JSON.parse似乎适用于对象和数组,所以如果可以的话,我会尝试以这些方式之一传递您的数据,如果不能,请坚持使用您的解决方法。