从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