模块 JSON

JavaScript 对象表示法 (JSON)

JSON 是一种轻量级的数据交换格式。

一个 JSON 值是以下之一:

  • 双引号文本:"foo"

  • 数字:11.02.0e2

  • 布尔值:truefalse

  • Null:null

  • 数组:值的有序列表,用方括号括起来。

    ["foo", 1, 1.0, 2.0e2, true, false, null]
    
  • 对象:名称/值对的集合,用花括号括起来;每个名称都是双引号文本;值可以是任何 JSON 值。

    {"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}
    

JSON 数组或对象可以包含嵌套的数组、对象和标量,深度不限。

{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}
[{"foo": 0, "bar": 1}, ["baz", 2]]

使用模块 JSON

要在您的代码中使用模块 JSON,请以以下代码开始:

require 'json'

此处的示例均假设已完成此操作。

解析 JSON

您可以使用以下两种方法之一来解析包含 JSON 数据的字符串:

  • JSON.parse(source, opts)

  • JSON.parse!(source, opts)

其中:

  • source 是一个 Ruby 对象。

  • opts 是一个哈希对象,其中包含控制允许的输入和输出格式的选项。

这两种方法的区别在于 JSON.parse! 省略了一些检查,对于某些 source 数据可能不安全;仅将其用于来自可信来源的数据。对于不太可信的来源,请使用更安全的方法 JSON.parse

解析 JSON 数组

source 是 JSON 数组时,默认情况下 JSON.parse 返回一个 Ruby 数组。

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

JSON 数组可以包含嵌套的数组、对象和标量,深度不限。

json = '[{"foo": 0, "bar": 1}, ["baz", 2]]'
JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]]

解析 JSON 对象

当源是 JSON 对象时,默认情况下 JSON.parse 返回一个 Ruby 哈希。

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

JSON 对象可以包含嵌套的数组、对象和标量,深度不限。

json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}'
JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]}

解析 JSON 标量

当源是 JSON 标量(不是数组或对象)时,JSON.parse 返回一个 Ruby 标量。

字符串

ruby = JSON.parse('"foo"')
ruby # => 'foo'
ruby.class # => String

整数

ruby = JSON.parse('1')
ruby # => 1
ruby.class # => Integer

浮点数

ruby = JSON.parse('1.0')
ruby # => 1.0
ruby.class # => Float
ruby = JSON.parse('2.0e2')
ruby # => 200
ruby.class # => Float

布尔值

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

解析选项

输入选项

选项 max_nesting (整数) 指定允许的最大嵌套深度;默认为 100;指定 false 以禁用深度检查。

使用默认值,false

source = '[0, [1, [2, [3]]]]'
ruby = JSON.parse(source)
ruby # => [0, [1, [2, [3]]]]

太深

# Raises JSON::NestingError (nesting of 2 is too deep):
JSON.parse(source, {max_nesting: 1})

错误的值

# Raises TypeError (wrong argument type Symbol (expected Fixnum)):
JSON.parse(source, {max_nesting: :foo})

选项 allow_nan (布尔值) 指定是否允许 source 中出现 NaNInfinityMinusInfinity;默认为 false

使用默认值,false

# Raises JSON::ParserError (225: unexpected token at '[NaN]'):
JSON.parse('[NaN]')
# Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
JSON.parse('[Infinity]')
# Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
JSON.parse('[-Infinity]')

允许

source = '[NaN, Infinity, -Infinity]'
ruby = JSON.parse(source, {allow_nan: true})
ruby # => [NaN, Infinity, -Infinity]
输出选项

选项 symbolize_names (布尔值) 指定返回的哈希键是否应为符号;默认为 false(使用字符串)。

使用默认值,false

source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
ruby = JSON.parse(source)
ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}

使用符号

ruby = JSON.parse(source, {symbolize_names: true})
ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}

选项 object_class (类) 指定用于每个 JSON 对象的 Ruby 类;默认为哈希。

使用默认值,哈希

source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
ruby = JSON.parse(source)
ruby.class # => Hash

使用 OpenStruct 类

ruby = JSON.parse(source, {object_class: OpenStruct})
ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>

选项 array_class (类) 指定用于每个 JSON 数组的 Ruby 类;默认为数组。

使用默认值,数组

source = '["foo", 1.0, true, false, null]'
ruby = JSON.parse(source)
ruby.class # => Array

使用集合类

ruby = JSON.parse(source, {array_class: Set})
ruby # => #<Set: {"foo", 1.0, true, false, nil}>

选项 create_additions (布尔值) 指定是否在解析中使用 JSON 附加功能。请参阅 JSON 附加功能

生成 JSON

要生成包含 JSON 数据的 Ruby 字符串,请使用方法 JSON.generate(source, opts),其中:

  • source 是一个 Ruby 对象。

  • opts 是一个哈希对象,其中包含控制允许的输入和输出格式的选项。

从数组生成 JSON

当源是 Ruby 数组时,JSON.generate 返回一个包含 JSON 数组的字符串。

ruby = [0, 's', :foo]
json = JSON.generate(ruby)
json # => '[0,"s","foo"]'

Ruby 数组可以包含嵌套的数组、哈希和标量,深度不限。

ruby = [0, [1, 2], {foo: 3, bar: 4}]
json = JSON.generate(ruby)
json # => '[0,[1,2],{"foo":3,"bar":4}]'

从哈希生成 JSON

当源是 Ruby 哈希时,JSON.generate 返回一个包含 JSON 对象的字符串。

ruby = {foo: 0, bar: 's', baz: :bat}
json = JSON.generate(ruby)
json # => '{"foo":0,"bar":"s","baz":"bat"}'

Ruby 哈希数组可以包含嵌套的数组、哈希和标量,深度不限。

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

从其他对象生成 JSON

当源既不是数组也不是哈希时,生成的 JSON 数据取决于源的类。

当源是 Ruby 整数或浮点数时,JSON.generate 返回一个包含 JSON 数字的字符串。

JSON.generate(42) # => '42'
JSON.generate(0.42) # => '0.42'

当源是 Ruby 字符串时,JSON.generate 返回一个包含 JSON 字符串(带双引号)的字符串。

JSON.generate('A string') # => '"A string"'

当源是 truefalsenil 时,JSON.generate 返回一个包含相应 JSON 标记的字符串。

JSON.generate(true) # => 'true'
JSON.generate(false) # => 'false'
JSON.generate(nil) # => 'null'

当源不是上述任何一种时,JSON.generate 返回一个包含源的 JSON 字符串表示形式的字符串。

JSON.generate(:foo) # => '"foo"'
JSON.generate(Complex(0, 0)) # => '"0+0i"'
JSON.generate(Dir.new('.')) # => '"#<Dir>"'

生成选项

输入选项

选项 allow_nan (布尔值) 指定是否可以生成 NaNInfinity-Infinity;默认为 false

使用默认值,false

# Raises JSON::GeneratorError (920: NaN not allowed in JSON):
JSON.generate(JSON::NaN)
# Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
JSON.generate(JSON::Infinity)
# Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
JSON.generate(JSON::MinusInfinity)

允许

ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity]
JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'

选项 max_nesting (整数) 指定 obj 中的最大嵌套深度;默认为 100

使用默认值,100

obj = [[[[[[0]]]]]]
JSON.generate(obj) # => '[[[[[[0]]]]]]'

太深

# Raises JSON::NestingError (nesting of 2 is too deep):
JSON.generate(obj, max_nesting: 2)
转义选项

选项 script_safe (布尔值) 指定是否应转义 '\u2028''\u2029''/',以使 JSON 对象可以安全地插入到 script 标记中。

选项 ascii_only (布尔值) 指定是否应转义 ASCII 范围之外的所有字符。

输出选项

默认格式化选项生成最紧凑的 JSON 数据,全部在一行上且没有空格。

您可以使用这些格式化选项以更开放的格式生成 JSON 数据,使用空格。另请参阅 JSON.pretty_generate

  • 选项 array_nl (字符串) 指定一个字符串(通常是换行符),在每个 JSON 数组后插入;默认为空字符串,''

  • 选项 object_nl (字符串) 指定一个字符串(通常是换行符),在每个 JSON 对象后插入;默认为空字符串,''

  • 选项 indent (字符串) 指定用于缩进的字符串(通常是空格);默认为空字符串,'';默认为空字符串,'';除非选项 array_nlobject_nl 指定换行符,否则无效。

  • 选项 space (字符串) 指定一个字符串(通常是空格),在每个 JSON 对象对中的冒号后插入;默认为空字符串,''

  • 选项 space_before (字符串) 指定一个字符串(通常是空格),在每个 JSON 对象对中的冒号前插入;默认为空字符串,''

在此示例中,obj 首先用于生成最短的 JSON 数据(无空格),然后再次使用所有指定的格式化选项。

obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
json = JSON.generate(obj)
puts 'Compact:', json
opts = {
  array_nl: "\n",
  object_nl: "\n",
  indent: '  ',
  space_before: ' ',
  space: ' '
}
puts 'Open:', JSON.generate(obj, opts)

输出

Compact:
{"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
Open:
{
  "foo" : [
    "bar",
    "baz"
],
  "bat" : {
    "bam" : 0,
    "bad" : 1
  }
}

JSON 附加功能

当您将非字符串对象从 Ruby“往返”到 JSON 并返回时,您将得到一个新的字符串,而不是您开始使用的对象。

ruby0 = Range.new(0, 2)
json = JSON.generate(ruby0)
json # => '0..2"'
ruby1 = JSON.parse(json)
ruby1 # => '0..2'
ruby1.class # => String

您可以使用 JSON *附加功能*来保留原始对象。该附加功能是 Ruby 类的扩展,因此:

  • JSON.generate 在 JSON 字符串中存储更多信息。

  • 调用时带有选项 create_additions 的 JSON.parse 使用该信息来创建正确的 Ruby 对象。

此示例显示了在 JSON 中生成的 Range 以及在有和没有 Range 附加功能的情况下解析回 Ruby 的过程。

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 = <<~EOT
  Generated JSON:
    Without addition:  #{json0} (#{json0.class})
    With addition:     #{json1} (#{json1.class})
  Parsed JSON:
    Without addition:  #{ruby0.inspect} (#{ruby0.class})
    With addition:     #{ruby1.inspect} (#{ruby1.class})
EOT
puts display

此输出显示不同的结果。

Generated JSON:
  Without addition:  "0..2" (String)
  With addition:     {"json_class":"Range","a":[0,2,false]} (String)
Parsed JSON:
  Without addition:  "0..2" (String)
  With addition:     0..2 (Range)

JSON 模块包含某些类的附加功能。您还可以制作自定义附加功能。请参阅 自定义 JSON 附加功能

内置附加功能

JSON 模块包含某些类的附加功能。要使用附加功能,请 require 其源:

  • BigDecimal: 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'

为了减少标点符号的混乱,以下示例通过 puts 显示生成的 JSON,而不是通常的 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(及其子类,包括 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') # #<OpenStruct name="Matz", language="Ruby">
json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}}
ruby1 = JSON.parse(json, create_additions: true) # #<OpenStruct name="Matz", language="Ruby">
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]) # #<Set: {0, 1, 2}>
json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]}
ruby1 = JSON.parse(json, create_additions: true) # #<Set: {0, 1, 2}>
ruby1.class # Set

Struct

require 'json/add/struct'
Customer = Struct.new(:name, :address) # Customer
ruby0 = Customer.new("Dave", "123 Main") # #<struct Customer name="Dave", address="123 Main">
json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]}
ruby1 = JSON.parse(json, create_additions: true) # #<struct Customer name="Dave", address="123 Main">
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

自定义 JSON 附加功能

除了提供的 JSON 附加功能之外,您还可以制作自己的 JSON 附加功能,无论是用于 Ruby 内置类还是用于用户定义的类。

这是一个用户定义的类 Foo

class Foo
  attr_accessor :bar, :baz
  def initialize(bar, baz)
    self.bar = bar
    self.baz = baz
  end
end

这是它的 JSON 附加功能:

# 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

演示

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 = <<~EOT
  Generated JSON:
    Without custom addition:  #{json0} (#{json0.class})
    With custom addition:     #{json1} (#{json1.class})
  Parsed JSON:
    Without custom addition:  #{obj0.inspect} (#{obj0.class})
    With custom addition:     #{obj1.inspect} (#{obj1.class})
EOT
puts display

输出

Generated JSON:
  Without custom addition:  "#<Foo:0x0000000006534e80>" (String)
  With custom addition:     {"json_class":"Foo","a":[0,1]} (String)
Parsed JSON:
  Without custom addition:  "#<Foo:0x0000000006534e80>" (String)
  With custom addition:     #<Foo:0x0000000006473bb8 @bar=0, @baz=1> (Foo)

常量

Infinity
JSON_LOADED
MinusInfinity
NOT_SET
NaN
VERSION

属性

dump_default_options[RW]

设置或返回 JSON.dump 方法的默认选项。初始状态:

opts = JSON.dump_default_options
opts # => {:max_nesting=>false, :allow_nan=>true}
generator[R]

返回 JSON 使用的 JSON 生成器模块。

load_default_options[RW]

设置或返回 JSON.load 方法的默认选项。初始状态:

opts = JSON.load_default_options
opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
parser[R]

返回 JSON 使用的 JSON 解析器类。

state[RW]

设置或返回 JSON 使用的 JSON 生成器状态类。

unsafe_load_default_options[RW]

设置或返回 JSON.unsafe_load 方法的默认选项。初始值为

opts = JSON.load_default_options
opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}

公共类方法

JSON[object] → new_array 或 new_string 点击以切换源代码

如果 object 是一个字符串,则使用 objectopts 调用 JSON.parse (参见方法 parse)

json = '[0, 1, null]'
JSON[json]# => [0, 1, nil]

否则,使用 objectopts 调用 JSON.generate (参见方法 generate)

ruby = [0, 1, nil]
JSON[ruby] # => '[0,1,null]'
# File json/lib/json/common.rb, line 23
def [](object, opts = {})
  if object.is_a?(String)
    return JSON.parse(object, opts)
  elsif object.respond_to?(:to_str)
    str = object.to_str
    if str.is_a?(String)
      return JSON.parse(str, opts)
    end
  end

  JSON.generate(object, opts)
end
create_fast_state() 点击以切换源代码
# File json/lib/json/common.rb, line 80
def create_fast_state
  State.new(
    :indent         => '',
    :space          => '',
    :object_nl      => "",
    :array_nl       => "",
    :max_nesting    => false
  )
end
create_id() 点击以切换源代码

返回当前的创建标识符。另请参见 JSON.create_id=

# File json/lib/json/common.rb, line 115
def self.create_id
  Thread.current[:"JSON.create_id"] || 'json_class'
end
create_id=(new_value) 点击以切换源代码

设置创建标识符,用于决定是否应该调用类的 json_create 钩子;初始值为 json_class

JSON.create_id # => 'json_class'
# File json/lib/json/common.rb, line 109
def self.create_id=(new_value)
  Thread.current[:"JSON.create_id"] = new_value.dup.freeze
end
create_pretty_state() 点击以切换源代码
# File json/lib/json/common.rb, line 90
def create_pretty_state
  State.new(
    :indent         => '  ',
    :space          => ' ',
    :object_nl      => "\n",
    :array_nl       => "\n"
  )
end
iconv(to, from, string) 点击以切换源代码

使用 String.encode 对字符串进行编码。

# File json/lib/json/common.rb, line 832
def self.iconv(to, from, string)
  string.encode(to, from)
end
restore(source, proc = nil, options = nil)
别名:load

公共实例方法

dump(obj, io = nil, limit = nil) 点击以切换源代码

obj 转储为 JSON 字符串,即在对象上调用 generate 并返回结果。

默认选项可以通过方法 JSON.dump_default_options 更改。

  • 如果给出了参数 io,则应该响应方法 write;JSON 字符串会被写入到 io,并返回 io。如果未给出 io,则返回 JSON 字符串。

  • 如果给出了参数 limit,则会将其作为选项 max_nesting 传递给 JSON.generate


当未给出参数 io 时,返回从 obj 生成的 JSON 字符串

obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
json = JSON.dump(obj)
json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"

当给出参数 io 时,将 JSON 字符串写入到 io 并返回 io

path = 't.json'
File.open(path, 'w') do |file|
  JSON.dump(obj, file)
end # => #<File:t.json (closed)>
puts File.read(path)

输出

{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
# File json/lib/json/common.rb, line 795
def dump(obj, anIO = nil, limit = nil, kwargs = nil)
  if kwargs.nil?
    if limit.nil?
      if anIO.is_a?(Hash)
        kwargs = anIO
        anIO = nil
      end
    elsif limit.is_a?(Hash)
      kwargs = limit
      limit = nil
    end
  end

  unless anIO.nil?
    if anIO.respond_to?(:to_io)
      anIO = anIO.to_io
    elsif limit.nil? && !anIO.respond_to?(:write)
      anIO, limit = nil, anIO
    end
  end

  opts = JSON.dump_default_options
  opts = opts.merge(:max_nesting => limit) if limit
  opts = merge_dump_options(opts, **kwargs) if kwargs

  begin
    if State === opts
      opts.generate(obj, anIO)
    else
      State.generate(obj, opts, anIO)
    end
  rescue JSON::NestingError
    raise ArgumentError, "exceed depth limit"
  end
end
fast_generate(obj, opts) → new_string 点击以切换源代码

此处的参数 objoptsJSON.generate 中的参数 objopts 相同。

默认情况下,生成 JSON 数据时不会检查 obj 中的循环引用(选项 max_nesting 设置为 false,禁用)。

如果 obj 包含循环引用,则会引发异常

a = []; b = []; a.push(b); b.push(a)
# Raises SystemStackError (stack level too deep):
JSON.fast_generate(a)
# File json/lib/json/common.rb, line 329
def fast_generate(obj, opts = nil)
  if State === opts
    state = opts
  else
    state = JSON.create_fast_state.configure(opts)
  end
  state.generate(obj)
end
generate(obj, opts = nil) → new_string 点击以切换源代码

返回一个包含生成的 JSON 数据的字符串。

另请参见 JSON.fast_generateJSON.pretty_generate

参数 obj 是要转换为 JSON 的 Ruby 对象。

如果给出参数 opts,则包含用于生成的选项哈希。参见生成选项


obj 是一个数组时,返回一个包含 JSON 数组的字符串

obj = ["foo", 1.0, true, false, nil]
json = JSON.generate(obj)
json # => '["foo",1.0,true,false,null]'

obj 是一个哈希时,返回一个包含 JSON 对象的字符串

obj = {foo: 0, bar: 's', baz: :bat}
json = JSON.generate(obj)
json # => '{"foo":0,"bar":"s","baz":"bat"}'

有关从其他 Ruby 对象生成的示例,请参见从其他对象生成 JSON


如果任何格式化选项不是字符串,则会引发异常。

如果 obj 包含循环引用,则会引发异常

a = []; b = []; a.push(b); b.push(a)
# Raises JSON::NestingError (nesting of 100 is too deep):
JSON.generate(a)
# File json/lib/json/common.rb, line 301
def generate(obj, opts = nil)
  if State === opts
    opts.generate(obj)
  else
    State.generate(obj, opts, nil)
  end
end
load(source, proc = nil, options = {}) → object 点击以切换源代码

返回通过解析给定的 source 创建的 Ruby 对象。

注意:此方法旨在序列化来自受信任用户输入的数据,例如来自您自己的数据库服务器或您控制下的客户端的数据,允许不受信任的用户将 JSON 源传递给它可能是危险的。如果必须使用它,请改用 JSON.unsafe_load 以明确说明。

JSON 2.8.0 版本以来,当反序列化非原生类型时,如果未显式启用 `create_additions`,则 `load` 会发出弃用警告,并且在 JSON 3.0 版本中,`load` 默认会禁用 `create_additions`。

  • 参数 source 必须是字符串,或者可以转换为字符串

    • 如果 source 响应实例方法 to_str,则 source.to_str 将成为源。

    • 如果 source 响应实例方法 to_io,则 source.to_io.read 将成为源。

    • 如果 source 响应实例方法 read,则 source.read 将成为源。

    • 如果以下两个条件都为真,则源将变为字符串 'null'

      • 选项 allow_blank 指定一个真值。

      • 如上定义的源是 nil 或空字符串 ''

    • 否则,source 仍然是源。

  • 如果给出参数 proc,则必须是一个接受一个参数的 Proc。它将使用每个结果(深度优先顺序)递归调用。请参阅下面的详细信息。

  • 如果给出参数 opts,则包含用于解析的选项哈希。参见解析选项。默认选项可以通过方法 JSON.load_default_options= 更改。


当未给出 proc 时,会如上修改 source 并返回 parse(source, opts) 的结果;参见 parse

以下示例的源代码

source = <<~JSON
  {
    "name": "Dave",
    "age" :40,
    "hats": [
      "Cattleman's",
      "Panama",
      "Tophat"
    ]
  }
JSON

加载字符串

ruby = JSON.load(source)
ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

加载 IO 对象

require 'stringio'
object = JSON.load(StringIO.new(source))
object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

加载文件对象

path = 't.json'
File.write(path, source)
File.open(path) do |file|
  JSON.load(file)
end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

当给出 proc

  • 如上修改 source

  • 从调用 parse(source, opts) 获取 result

  • 递归调用 proc(result)

  • 返回最终结果。

示例

require 'json'

# Some classes for the example.
class Base
  def initialize(attributes)
    @attributes = attributes
  end
end
class User    < Base; end
class Account < Base; end
class Admin   < Base; end
# The JSON source.
json = <<-EOF
{
  "users": [
      {"type": "User", "username": "jane", "email": "[email protected]"},
      {"type": "User", "username": "john", "email": "[email protected]"}
  ],
  "accounts": [
      {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
      {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
  ],
  "admins": {"type": "Admin", "password": "0wn3d"}
}
EOF
# Deserializer method.
def deserialize_obj(obj, safe_types = %w(User Account Admin))
  type = obj.is_a?(Hash) && obj["type"]
  safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
end
# Call to JSON.load
ruby = JSON.load(json, proc {|obj|
  case obj
  when Hash
    obj.each {|k, v| obj[k] = deserialize_obj v }
  when Array
    obj.map! {|v| deserialize_obj v }
  end
})
pp ruby

输出

{"users"=>
   [#<User:0x00000000064c4c98
     @attributes=
       {"type"=>"User", "username"=>"jane", "email"=>"[email protected]"}>,
     #<User:0x00000000064c4bd0
     @attributes=
       {"type"=>"User", "username"=>"john", "email"=>"[email protected]"}>],
 "accounts"=>
   [{"account"=>
       #<Account:0x00000000064c4928
       @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
    {"account"=>
       #<Account:0x00000000064c4680
       @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
 "admins"=>
   #<Admin:0x00000000064c41f8
   @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
# File json/lib/json/common.rb, line 714
def load(source, proc = nil, options = nil)
  opts = if options.nil?
    load_default_options
  else
    load_default_options.merge(options)
  end

  unless source.is_a?(String)
    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
  end

  if opts[:allow_blank] && (source.nil? || source.empty?)
    source = 'null'
  end
  result = parse(source, opts)
  recurse_proc(result, &proc) if proc
  result
end
也别名为:restore
load_file(path, opts={}) → object 点击以切换源代码

调用

parse(File.read(path), opts)

参见方法 parse

# File json/lib/json/common.rb, line 250
def load_file(filespec, opts = nil)
  parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
end
load_file!(path, opts = {}) 点击以切换源代码

调用

JSON.parse!(File.read(path, opts))

参见方法 parse!

# File json/lib/json/common.rb, line 261
def load_file!(filespec, opts = {})
  parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
end
merge_dump_options(opts, strict: NOT_SET) 点击以切换源代码
# File json/lib/json/common.rb, line 836
def merge_dump_options(opts, strict: NOT_SET)
  opts = opts.merge(strict: strict) if NOT_SET != strict
  opts
end
parse(source, opts) → object 点击以切换源代码

返回通过解析给定的 source 创建的 Ruby 对象。

参数 source 包含要解析的字符串。

如果给出参数 opts,则包含用于解析的选项哈希。参见解析选项


source 是 JSON 数组时,返回一个 Ruby 数组

source = '["foo", 1.0, true, false, null]'
ruby = JSON.parse(source)
ruby # => ["foo", 1.0, true, false, nil]
ruby.class # => Array

source 是 JSON 对象时,返回一个 Ruby 哈希

source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
ruby = JSON.parse(source)
ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
ruby.class # => Hash

有关解析所有 JSON 数据类型的示例,请参见解析 JSON

解析嵌套的 JSON 对象

source = <<~JSON
  {
  "name": "Dave",
    "age" :40,
    "hats": [
      "Cattleman's",
      "Panama",
      "Tophat"
    ]
  }
JSON
ruby = JSON.parse(source)
ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

如果 source 不是有效的 JSON,则会引发异常

# Raises JSON::ParserError (783: unexpected token at ''):
JSON.parse('')
# File json/lib/json/common.rb, line 220
def parse(source, opts = nil)
  Parser.parse(source, opts)
end
parse!(source, opts) → object 点击以切换源代码

调用

parse(source, opts)

使用 source 和可能修改的 opts

JSON.parse 的区别

  • 如果未提供选项 max_nesting,则默认为 false,这将禁用对嵌套深度的检查。

  • 如果未提供选项 allow_nan,则默认为 true

# File json/lib/json/common.rb, line 235
def parse!(source, opts = {})
  opts = {
    :max_nesting  => false,
    :allow_nan    => true
  }.merge(opts)
  Parser.new(source, **(opts||{})).parse
end
pretty_generate(obj, opts = nil) → new_string 点击以切换源代码

此处的参数 objoptsJSON.generate 中的参数 objopts 相同。

默认选项为

{
  indent: '  ',   # Two spaces
  space: ' ',     # One space
  array_nl: "\n", # Newline
  object_nl: "\n" # Newline
}

示例

obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
json = JSON.pretty_generate(obj)
puts json

输出

{
  "foo": [
    "bar",
    "baz"
  ],
  "bat": {
    "bam": 0,
    "bad": 1
  }
}
# File json/lib/json/common.rb, line 374
def pretty_generate(obj, opts = nil)
  if State === opts
    state, opts = opts, nil
  else
    state = JSON.create_pretty_state
  end
  if opts
    if opts.respond_to? :to_hash
      opts = opts.to_hash
    elsif opts.respond_to? :to_h
      opts = opts.to_h
    else
      raise TypeError, "can't convert #{opts.class} into Hash"
    end
    state.configure(opts)
  end
  state.generate(obj)
end
unsafe_load(source, proc = nil, options = {}) → object 点击以切换源代码

返回通过解析给定的 source 创建的 Ruby 对象。

注意:此方法旨在序列化来自受信任用户输入的数据,例如来自您自己的数据库服务器或您控制下的客户端的数据,允许不受信任的用户将 JSON 源传递给它可能是危险的。

  • 参数 source 必须是字符串,或者可以转换为字符串

    • 如果 source 响应实例方法 to_str,则 source.to_str 将成为源。

    • 如果 source 响应实例方法 to_io,则 source.to_io.read 将成为源。

    • 如果 source 响应实例方法 read,则 source.read 将成为源。

    • 如果以下两个条件都为真,则源将变为字符串 'null'

      • 选项 allow_blank 指定一个真值。

      • 如上定义的源是 nil 或空字符串 ''

    • 否则,source 仍然是源。

  • 如果给出参数 proc,则必须是一个接受一个参数的 Proc。它将使用每个结果(深度优先顺序)递归调用。请参阅下面的详细信息。

  • 如果给出参数 opts,则包含用于解析的选项哈希。参见解析选项。默认选项可以通过方法 JSON.unsafe_load_default_options= 更改。


当未给出 proc 时,会如上修改 source 并返回 parse(source, opts) 的结果;参见 parse

以下示例的源代码

source = <<~JSON
  {
    "name": "Dave",
    "age" :40,
    "hats": [
      "Cattleman's",
      "Panama",
      "Tophat"
    ]
  }
JSON

加载字符串

ruby = JSON.unsafe_load(source)
ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

加载 IO 对象

require 'stringio'
object = JSON.unsafe_load(StringIO.new(source))
object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

加载文件对象

path = 't.json'
File.write(path, source)
File.open(path) do |file|
  JSON.unsafe_load(file)
end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

当给出 proc

  • 如上修改 source

  • 从调用 parse(source, opts) 获取 result

  • 递归调用 proc(result)

  • 返回最终结果。

示例

require 'json'

# Some classes for the example.
class Base
  def initialize(attributes)
    @attributes = attributes
  end
end
class User    < Base; end
class Account < Base; end
class Admin   < Base; end
# The JSON source.
json = <<-EOF
{
  "users": [
      {"type": "User", "username": "jane", "email": "[email protected]"},
      {"type": "User", "username": "john", "email": "[email protected]"}
  ],
  "accounts": [
      {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
      {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
  ],
  "admins": {"type": "Admin", "password": "0wn3d"}
}
EOF
# Deserializer method.
def deserialize_obj(obj, safe_types = %w(User Account Admin))
  type = obj.is_a?(Hash) && obj["type"]
  safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
end
# Call to JSON.unsafe_load
ruby = JSON.unsafe_load(json, proc {|obj|
  case obj
  when Hash
    obj.each {|k, v| obj[k] = deserialize_obj v }
  when Array
    obj.map! {|v| deserialize_obj v }
  end
})
pp ruby

输出

{"users"=>
   [#<User:0x00000000064c4c98
     @attributes=
       {"type"=>"User", "username"=>"jane", "email"=>"[email protected]"}>,
     #<User:0x00000000064c4bd0
     @attributes=
       {"type"=>"User", "username"=>"john", "email"=>"[email protected]"}>],
 "accounts"=>
   [{"account"=>
       #<Account:0x00000000064c4928
       @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
    {"account"=>
       #<Account:0x00000000064c4680
       @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
 "admins"=>
   #<Admin:0x00000000064c41f8
   @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
# File json/lib/json/common.rb, line 554
def unsafe_load(source, proc = nil, options = nil)
  opts = if options.nil?
    unsafe_load_default_options
  else
    unsafe_load_default_options.merge(options)
  end

  unless source.is_a?(String)
    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
  end

  if opts[:allow_blank] && (source.nil? || source.empty?)
    source = 'null'
  end
  result = parse(source, opts)
  recurse_proc(result, &proc) if proc
  result
end

私有实例方法

restore(source, proc = nil, options = nil)
别名:load