Wen
Wen

Reputation: 1220

What's the difference between JSON.load and JSON.parse methods of Ruby lib?

From the ruby document I can see that load method takes a proc as arg while parse doesn't. Is there any other difference? Say, when I have a string of JSON, which method should I use to turn it into a Ruby object?

load(source, proc = nil, options = {}) Load a ruby data structure from a JSON source and return it. A source can either be a string-like object, an IO-like object, or an object responding to the read method. If proc was given, it will be called with any nested Ruby object as an argument recursively in depth first order. To modify the default options pass in the optional options argument as well. This method is part of the implementation of the load/dump interface of Marshal and YAML. Also aliased as: restore

parse(source, opts = {}) Parse the JSON document source into a Ruby data structure and return it.

Upvotes: 43

Views: 30615

Answers (5)

TorvaldsDB
TorvaldsDB

Reputation: 982

Here load the source codes 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

The first line within the method:

opts = load_default_options.merge options

We can call the JSON#load_default_options in the console:

JSON.load_default_options

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

We can see there're four default options, what do they mean, we can get some from here click me:

  • max_nesting: The maximum depth of nesting allowed in the parsed data structures. Disable depth checking with :max_nesting => false. It defaults to 100./
  • allow_nan: If set to true, allow NaN, Infinity and -Infinity in defiance of RFC 7159 to be parsed by the Parser. This option defaults to false.
  • symbolize_names: If set to true, returns symbols for the names (keys) in a JSON object. Otherwise, strings are returned. Strings are the default.
  • create_additions: If set to false, the Parser doesn't create additions even if a matching class and create_id was found. This option defaults to false.
  • object_class: Defaults to Hash
  • array_class: Defaults to Array

WHY

referring to JSON#parse, back to see the source codes of JSON#load, The third to last line, there is result = parse(source, opts), So load actually is a parse with four default options.

That is the reason:

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

another way, if the object to be parsed responds to to_io meaning is a File, load still makes sense. However, parse do not.

Upvotes: 2

Giuse
Giuse

Reputation: 126

Yet another difference: different options.
Common-use example (note _keys vs. _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

Upvotes: 0

Flexoid
Flexoid

Reputation: 4245

One more difference is that JSON.load parses single value (not object and not array) by default.

JSON.load("false")
=> false

JSON.load("123")
=> 123

But JSON.parse requires quirks mode to be enabled to parse such value.

JSON.parse("false")
JSON::ParserError: 757: unexpected token at 'false'

JSON.parse("false", quirks_mode: true)
=> false

Upvotes: 20

Frederick Cheung
Frederick Cheung

Reputation: 84134

A key difference is that JSON.load is unsafe when given untrusted input (the same can be achieved with JSON.parse, but it has safe defaults). This is because it provides a way to instantiate classes other than the "normal" Hash, String, Array, Number classes:

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

whereas

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

This intended for you to implement custom serialization of data - it shouldn't be used when parsing data from the wide world. Of course you do need to implement the json_creatable? and json_create methods for this to actually achieve anything, but how confident are you that none of your dependencies do this or implement method_missing in such a way that it could be misused ? (see for example the Marshal.load exploits. As a result of this JSON.load & JSON.parse were tightened up significantly).

Always use JSON.parse when dealing with untrusted data or unless you need the extra capabilities of JSON.load

Upvotes: 27

Kashyap
Kashyap

Reputation: 4796

JSON#parse parses a JSON string into a Ruby Hash.

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

JSON#load takes either a string or IO (file etc) and converts that to Ruby Hash/Array

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

In fact, it converts any object that responds to a #read method. For example:

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

  def read
    @a
  end
end

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

Upvotes: 38

Related Questions