Ruby lib 的 JSON.load 和 JSON.parse 方法有什么区别?



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

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

parse(source, opts = {})将JSON文档源解析为Ruby数据结构并返回。

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

 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在给定不受信任的输入时是不安全的(JSON.parse也可以实现,但它具有安全的默认值)。这是因为它提供了一种实例化类的方法,而不是"普通的"哈希、字符串、数组、数字类:

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.load &JSON.parse明显收紧)。

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

另一个区别是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

这里load源代码点击我

# 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}

我们可以看到有四个默认选项,它们是什么意思,我们可以从这里获取一些点击我:

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

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

这就是原因:

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

另一种方式,如果要解析的对象响应to_io的含义是一个文件,那么load仍然有意义。然而,解析不需要。

还有一个区别:不同的选项。
常用示例(注意_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

相关内容

  • 没有找到相关文章

最新更新