【问题标题】:What's the difference between JSON.load and JSON.parse methods of Ruby lib?Ruby lib 的 JSON.load 和 JSON.parse 方法有什么区别?
【发布时间】:2013-06-18 01:30:58
【问题描述】:

从 ruby​​ 文档中我可以看到 load 方法将 proc 用作 arg 而 parse 没有。还有其他区别吗?比如说,当我有一个 JSON 字符串时,我应该使用哪种方法将其转换为 Ruby 对象?

加载(源,过程 = nil,选项 = {}) 从 JSON 源加载一个 ruby​​ 数据结构并返回它。源可以是类字符串对象、类 IO 对象或响应读取方法的对象。如果给出了 proc,它将以任何嵌套的 Ruby 对象作为参数以深度一阶递归方式调用。要修改默认选项,还要传入可选选项参数。 此方法是 Marshal 和 YAML 的加载/转储接口实现的一部分。 也有别名:restore

解析(来源,选择 = {}) 将 JSON 文档源解析为 Ruby 数据结构并返回。

【问题讨论】:

  • "。如果给出了 proc,它将以任何嵌套的 Ruby 对象作为参数以深度一阶递归方式调用"
  • 将 JSON 的接口与 Marshal 的接口进行比较,然后再次阅读 load 文档。然后看load source
  • 嗨,谢谢。我知道proc 是可选的。我收到这个问题是因为我曾经通过 JSON.parse 转换 JSON 字符串,但我观看了一个使用 JSON.load open('http://some.url').read 的截屏视频。

标签: ruby json


【解决方案1】:

JSON#parse 将 JSON 字符串解析为 Ruby 哈希。

 JSON.parse('{"name": "Some Name"}') # => {"name" => "Some Name"}

JSON#load 接受字符串或 IO(文件等)并将其转换为 Ruby 哈希/数组

JSON.load File.new("names.json")     # => Reads the JSON inside the file and results in a Ruby Object.

JSON.load '{"name": "Some Name"}'    # Works just like #parse

事实上,它转换任何响应#read 方法的对象。例如:

class A
  def initialize
    @a = '{"name": "Some Name"}'
  end

  def read
    @a
  end
end

JSON.load(A.new)                      # => {"name" => "Some Name"}

【讨论】:

  • 我很困惑,因为看到有人使用JSON.load open('http://...').read。现在我知道parseload 都在String 上工作。感谢您的帮助。
  • 是的。 open-uri 具有从链接返回 IO 对象的 open 方法。
【解决方案2】:

一个关键的区别是JSON.load 在给定不受信任的输入时是不安全的(JSON.parse 也可以达到同样的效果,但它有安全的默认值)。这是因为它提供了一种方法来实例化“普通”Hash、String、Array、Number 类以外的类:

class Foo
  def self.json_creatable?
    true
  end
  def self.json_create attributes
    puts "called with #{attributes}"
  end
end

JSON.parse('{"json_class": "Foo"}') #=> {"json_class"=>"Foo"} 

JSON.load('{"json_class": "Foo"}')
called with {"json_class"=>"Foo"}
#=> nil

这旨在让您实现数据的自定义序列化 - 在解析来自广阔世界的数据时不应使用它。当然,您确实需要实现json_creatable?json_create 方法才能真正实现任何目标,但是您对您的任何依赖项都没有这样做或以可能被滥用的方式实现method_missing 有多大信心? (例如,参见 Marshal.load 漏洞利用。因此,JSON.loadJSON.parse 被显着收紧)。

在处理不受信任的数据或除非您需要JSON.load 的额外功能时,请始终使用JSON.parse

【讨论】:

    【解决方案3】:

    另一个区别是JSON.load默认解析单个值(不是对象也不是数组)。

    JSON.load("false")
    => false
    
    JSON.load("123")
    => 123
    

    JSON.parse 需要启用quirks mode 才能解析此类值。

    JSON.parse("false")
    JSON::ParserError: 757: unexpected token at 'false'
    
    JSON.parse("false", quirks_mode: true)
    => false
    

    【讨论】:

    • 这不再是真的。在 Ruby 2.4.1 JSON.parse("false") 工作得很好;我猜quirks_mode: true 成为默认值。
    【解决方案4】:

    这里load源代码click me

    # File ext/json/lib/json/common.rb, line 323
    def load(source, proc = nil, options = {})
      opts = load_default_options.merge options
      if source.respond_to? :to_str
        source = source.to_str
      elsif source.respond_to? :to_io
        source = source.to_io.read
      elsif source.respond_to?(:read)
        source = source.read
      end
      if opts[:allow_blank] && (source.nil? || source.empty?)
        source = 'null'
      end
      result = parse(source, opts)
      recurse_proc(result, &proc) if proc
      result
    end
    

    方法内的第一行:

    opts = load_default_options.merge options
    

    我们可以在控制台调用JSON#load_default_options

    JSON.load_default_options
    
    => {:max_nesting=>false, :allow_nan=>true, :quirks_mode=>true, :create_additions=>true}
    

    我们可以看到有四个默认选项,它们是什么意思,我们可以从这里得到一些click me

    • max_nesting:解析的数据结构中允许的最大嵌套深度。使用 :max_nesting => false 禁用深度检查。默认为 100。/
    • allow_nan:如果设置为 true,则允许 NaN、Infinity 和 -Infinity 无视 RFC 7159 被 Parser 解析。此选项默认为 false。
    • symbolize_names:如果设置为 true,则返回 JSON 对象中名称(键)的符号。否则,返回字符串。字符串是默认值。
    • create_additions:如果设置为 false,即使找到匹配的类和 create_id,解析器也不会创建添加。此选项默认为 false。
    • object_class:默认为哈希
    • array_class:默认为数组

    为什么

    参考JSON#parse,回看JSON#load的源码,倒数第三行,有result = parse(source, opts),所以load其实是一个parse,有四个默认选项。

    这就是原因:

    JSON.load("123") #=> 123
    JSON.parse("123", quirks_mode: true) #=> 123
    

    另一种方式,如果要解析的对象响应to_io 意思是一个文件,load 仍然有意义。但是,解析没有。

    【讨论】:

    • 这也表明JSON.load 不使用流式传输,因此与仅使用File.read 将文件的全部内容加载到内存中然后使用JSON.parse 解析相比没有性能或资源优势.
    【解决方案5】:

    另一个区别:不同的选项。
    常用示例(注意_keys_names):

    JSON.load '{"key": "val"}', symbolize_keys: true
    => {"key" => "val"}         # parse option syntax silently ignored 
    
    JSON.parse '{"key": "val"}', symbolize_keys: true
    => {:key => "val"}          # symbols, yay!
    
    JSON.load '{"key": "val"}', symbolize_names: true
    => {:key => "val"}          # using the correct syntax for load
    

    【讨论】:

    • JSON.load 不会忽略选项。它只需要 3 个参数,并且您提供的选项不正确。 JSON.load('{"key": "val"}', nil, symbolize_names: true) #=> {:key => "val"}
    • 你当然是对的,任何方法都需要自己的参数。然而,我的重点是这些选项的相似程度,以及不正确的选项(键)被默默忽略的事实。我浪费了一些时间来弄清楚我使用了错误的答案,所以我将此答案添加到我最初得到的第一个 SO 搜索结果中,希望下一个人的课程会更顺利:) 感谢您指出 ::symbolize_names
    • 感谢您记录它! :) 对不起,如果我听起来很简洁,只是不想让人们误解。
    猜你喜欢
    • 2015-12-30
    • 2017-02-04
    • 2012-04-28
    • 2010-10-11
    • 1970-01-01
    • 2010-11-14
    • 2013-08-12
    • 1970-01-01
    相关资源
    最近更新 更多