#frozen_string_literal: false require 'json/common' ## # = JavaScript \Object Notation (\JSON) # # \JSON is a lightweight data-interchange format. # # A \JSON value is one of the following: # - Double-quoted text: "foo". # - Number: +1+, +1.0+, +2.0e2+. # - Boolean: +true+, +false+. # - Null: +null+. # - \Array: an ordered list of values, enclosed by square brackets: # ["foo", 1, 1.0, 2.0e2, true, false, null] # # - \Object: a collection of name/value pairs, enclosed by curly braces; # each name is double-quoted text; # the values may be any \JSON values: # {"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null} # # A \JSON array or object may contain nested arrays, objects, and scalars # to any depth: # {"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]} # [{"foo": 0, "bar": 1}, ["baz", 2]] # # == Using \Module \JSON # # To make module \JSON available in your code, begin with: # require 'json' # # All examples here assume that this has been done. # # === Parsing \JSON # # You can parse a \String containing \JSON data using # either of two methods: # - JSON.parse(source, opts) # - JSON.parse!(source, opts) # # where # - +source+ is a Ruby object. # - +opts+ is a \Hash object containing options # that control both input allowed and output formatting. # # The difference between the two methods # is that JSON.parse! omits some checks # and may not be safe for some +source+ data; # use it only for data from trusted sources. # Use the safer method JSON.parse for less trusted sources. # # ==== Parsing \JSON Arrays # # When +source+ is a \JSON array, JSON.parse by default returns a Ruby \Array: # json = '["foo", 1, 1.0, 2.0e2, true, false, null]' # ruby = JSON.parse(json) # ruby # => ["foo", 1, 1.0, 200.0, true, false, nil] # ruby.class # => Array # # The \JSON array may contain nested arrays, objects, and scalars # to any depth: # json = '[{"foo": 0, "bar": 1}, ["baz", 2]]' # JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]] # # ==== Parsing \JSON \Objects # # When the source is a \JSON object, JSON.parse by default returns a Ruby \Hash: # json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}' # ruby = JSON.parse(json) # ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil} # ruby.class # => Hash # # The \JSON object may contain nested arrays, objects, and scalars # to any depth: # json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}' # JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]} # # ==== Parsing \JSON Scalars # # When the source is a \JSON scalar (not an array or object), # JSON.parse returns a Ruby scalar. # # \String: # ruby = JSON.parse('"foo"') # ruby # => "foo" # ruby.class # => String # \Integer: # ruby = JSON.parse('1') # ruby # => 1 # ruby.class # => Integer # \Float: # ruby = JSON.parse('1.0') # ruby # => 1.0 # ruby.class # => Float # ruby = JSON.parse('2.0e2') # ruby # => 200 # ruby.class # => Float # Boolean: # ruby = JSON.parse('true') # ruby # => true # ruby.class # => TrueClass # ruby = JSON.parse('false') # ruby # => false # ruby.class # => FalseClass # Null: # ruby = JSON.parse('null') # ruby # => nil # ruby.class # => NilClass # # === Generating \JSON # # To generate a Ruby \String containing \JSON data, # use method JSON.generate(source, opts), where # - +source+ is a Ruby object. # - +opts+ is a \Hash object containing options # that control both input allowed and output formatting. # # ==== Generating \JSON from Arrays # # When the source is a Ruby \Array, JSON.generate returns # a \String containing a \JSON array: # ruby = [0, 's', :foo] # json = JSON.generate(ruby) # json # => "[0,\"s\",\"foo\"]" # # The Ruby \Array array may contain nested arrays, hashes, and scalars # to any depth: # ruby = [0, [1, 2], {foo: 3, bar: 4}] # json = JSON.generate(ruby) # json # => "[0,[1,2],{\"foo\":3,\"bar\":4}]" # # ==== Generating \JSON from Hashes # # When the source is a Ruby \Hash, JSON.generate returns # a \String containing a \JSON object: # ruby = {foo: 0, bar: 's', baz: :bat} # json = JSON.generate(ruby) # json # => "{\"foo\":0,\"bar\":\"s\",\"baz\":\"bat\"}" # # The Ruby \Hash array may contain nested arrays, hashes, and scalars # to any depth: # ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad} # json = JSON.generate(ruby) # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}" # # ==== Generating \JSON from Other Objects # # When the source is neither an \Array nor a \Hash, # the generated \JSON data depends on the class of the source. # # When the source is a Ruby \Integer or \Float, JSON.generate returns # a \String containing a \JSON number: # JSON.generate(Integer(0)) # => "0"" # JSON.generate(Float(1.0)) # => "1.0" # # When the source is a Ruby \String, JSON.generate returns # a \String containing a \JSON string (with double-quotes): # JSON.generate('A string') # => "\"A string\"" # # When the source is +true+, +false+ or +nil+, JSON.generate returns # a \String containing the corresponding \JSON token: # JSON.generate(true) # => "true" # JSON.generate(false) # => "false" # JSON.generate(nil) # => "null" # # When the source is none of the above, JSON.generate returns # a \String containing a \JSON string representation of the source: # JSON.generate(:foo) # => "\"foo\"" # JSON.generate(Complex(0, 0)) # => "\"0+0i\"" # JSON.generate(Dir.new('.')) # => "\"#\"" # # == \JSON Additions # # When you "round trip" a non-\String object from Ruby to \JSON and back, # you have a new \String, instead of the object you began with: # ruby0 = Range.new(0, 2) # json = JSON.generate(ruby0) # json # => "\"0..2\"" # ruby1 = JSON.parse(json) # ruby1 # => "0..2" # ruby1.class # => String # # You can use \JSON _additions_ to preserve the original object. # The addition is an extension of a ruby class, so that: # - \JSON.generate stores more information in the \JSON string. # - \JSON.parse, called with option +create_additions+, # uses that information to create a proper Ruby object. # # This example shows a \Range being generated into \JSON # and parsed back into Ruby, both without and with # the addition for \Range: # ruby = Range.new(0, 2) # # This passage does not use the addition for Range. # json0 = JSON.generate(ruby) # ruby0 = JSON.parse(json0) # # This passage uses the addition for Range. # require 'json/add/range' # json1 = JSON.generate(ruby) # ruby1 = JSON.parse(json1, create_additions: true) # # Make a nice display. # display = <require 'json/add/bigdecimal' # - Complex: require 'json/add/complex' # - Date: require 'json/add/date' # - DateTime: require 'json/add/date_time' # - Exception: require 'json/add/exception' # - OpenStruct: require 'json/add/ostruct' # - Range: require 'json/add/range' # - Rational: require 'json/add/rational' # - Regexp: require 'json/add/regexp' # - Set: require 'json/add/set' # - Struct: require 'json/add/struct' # - Symbol: require 'json/add/symbol' # - Time: require 'json/add/time' # # To reduce punctuation clutter, the examples below # show the generated \JSON via +puts+, rather than the usual +inspect+, # # \BigDecimal: # require 'json/add/bigdecimal' # ruby0 = BigDecimal(0) # 0.0 # json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"} # ruby1 = JSON.parse(json, create_additions: true) # 0.0 # ruby1.class # => BigDecimal # # \Complex: # require 'json/add/complex' # ruby0 = Complex(1+0i) # 1+0i # json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0} # ruby1 = JSON.parse(json, create_additions: true) # 1+0i # ruby1.class # Complex # # \Date: # require 'json/add/date' # ruby0 = Date.today # 2020-05-02 # json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0} # ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 # ruby1.class # Date # # \DateTime: # require 'json/add/date_time' # ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00 # json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0} # ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00 # ruby1.class # DateTime # # \Exception (and its subclasses including \RuntimeError): # require 'json/add/exception' # ruby0 = Exception.new('A message') # A message # json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null} # ruby1 = JSON.parse(json, create_additions: true) # A message # ruby1.class # Exception # ruby0 = RuntimeError.new('Another message') # Another message # json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null} # ruby1 = JSON.parse(json, create_additions: true) # Another message # ruby1.class # RuntimeError # # \OpenStruct: # require 'json/add/ostruct' # ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # # # json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}} # ruby1 = JSON.parse(json, create_additions: true) # # # ruby1.class # OpenStruct # # \Range: # require 'json/add/range' # ruby0 = Range.new(0, 2) # 0..2 # json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]} # ruby1 = JSON.parse(json, create_additions: true) # 0..2 # ruby1.class # Range # # \Rational: # require 'json/add/rational' # ruby0 = Rational(1, 3) # 1/3 # json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3} # ruby1 = JSON.parse(json, create_additions: true) # 1/3 # ruby1.class # Rational # # \Regexp: # require 'json/add/regexp' # ruby0 = Regexp.new('foo') # (?-mix:foo) # json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"} # ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo) # ruby1.class # Regexp # # \Set: # require 'json/add/set' # ruby0 = Set.new([0, 1, 2]) # # # json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]} # ruby1 = JSON.parse(json, create_additions: true) # # # ruby1.class # Set # # \Struct: # require 'json/add/struct' # Customer = Struct.new(:name, :address) # Customer # ruby0 = Customer.new("Dave", "123 Main") # # # json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]} # ruby1 = JSON.parse(json, create_additions: true) # # # ruby1.class # Customer # # \Symbol: # require 'json/add/symbol' # ruby0 = :foo # foo # json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"} # ruby1 = JSON.parse(json, create_additions: true) # foo # ruby1.class # Symbol # # \Time: # require 'json/add/time' # ruby0 = Time.now # 2020-05-02 11:28:26 -0500 # json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000} # ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500 # ruby1.class # Time # # # === Custom \JSON Additions # # In addition to the \JSON additions provided, # you can craft \JSON additions of your own, # either for Ruby built-in classes or for user-defined classes. # # Here's a user-defined class +Foo+: # class Foo # attr_accessor :bar, :baz # def initialize(bar, baz) # self.bar = bar # self.baz = baz # end # end # # Here's the \JSON addition for it: # # Extend class Foo with JSON addition. # class Foo # # Serialize Foo object with its class name and arguments # def to_json(*args) # { # JSON.create_id => self.class.name, # 'a' => [ bar, baz ] # }.to_json(*args) # end # # Deserialize JSON string by constructing new Foo object with arguments. # def self.json_create(object) # new(*object['a']) # end # end # # Demonstration: # require 'json' # # This Foo object has no custom addition. # foo0 = Foo.new(0, 1) # json0 = JSON.generate(foo0) # obj0 = JSON.parse(json0) # # Lood the custom addition. # require_relative 'foo_addition' # # This foo has the custom addition. # foo1 = Foo.new(0, 1) # json1 = JSON.generate(foo1) # obj1 = JSON.parse(json1, create_additions: true) # # Make a nice display. # display = <" (String) # With custom addition: {"json_class":"Foo","a":[0,1]} (String) # Parsed JSON: # Without custom addition: "#" (String) # With custom addition: # (Foo) # module JSON require 'json/version' begin require 'json/ext' rescue LoadError require 'json/pure' end end