module JSON
JavaScript Object Notation (JSON)
JSON 是一种轻量级的数据交换格式。
JSON 值是以下之一
-
双引号文本:
"foo"。 -
数字:
1、1.0、2.0e2。 -
布尔值:
true、false。 -
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]]
使用 Module JSON
要使 module JSON 在您的代码中可用,请以以下方式开始
require 'json'
此处的所有示例均假定已完成此操作。
解析 JSON
您可以使用以下两种方法之一解析包含 JSON 数据的字符串
其中
-
source是一个 Ruby 对象。 -
opts是一个 Hash 对象,包含控制输入允许和输出格式的选项。
两种方法之间的区别在于 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 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
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 标量。
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
布尔值
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 (Integer) 指定允许的最大嵌套深度;默认为 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_duplicate_key 指定对象中的重复键是应被忽略还是导致抛出错误
未指定时
# The last value is used and a deprecation warning emitted.
JSON.parse('{"a": 1, "a":2}') => {"a" => 2}
# warning: detected duplicate keys in JSON object.
# This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
设置为 ‘true` 时
# The last value is used.
JSON.parse('{"a": 1, "a":2}') => {"a" => 2}
设置为 ‘false` 时,未来默认值
JSON.parse('{"a": 1, "a":2}') => duplicate key at line 1 column 1 (JSON::ParserError)
选项 allow_nan (boolean) 指定是否允许在 source 中使用 NaN、Infinity 和 MinusInfinity;默认为 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]
选项 allow_trailing_comma (boolean) 指定是否允许对象和数组中的尾随逗号;默认为 false。
默认情况下,false
JSON.parse('[1,]') # unexpected character: ']' at line 1 column 4 (JSON::ParserError)
启用时
JSON.parse('[1,]', allow_trailing_comma: true) # => [1]
输出选项
选项 freeze (boolean) 指定返回的对象是否将被冻结;默认为 false。
选项 symbolize_names (boolean) 指定返回的 Hash 键是否应为 Symbol;默认为 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}
使用 Symbol
ruby = JSON.parse(source, {symbolize_names: true}) ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
选项 object_class (Class) 指定将用于每个 JSON 对象的 Ruby 类;默认为 Hash。
默认情况下,Hash
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 (Class) 指定将用于每个 JSON 数组的 Ruby 类;默认为 Array。
默认情况下,Array
source = '["foo", 1.0, true, false, null]' ruby = JSON.parse(source) ruby.class # => Array
使用类 Set
ruby = JSON.parse(source, {array_class: Set}) ruby # => #<Set: {"foo", 1.0, true, false, nil}>
选项 create_additions (boolean) 指定是否在解析中使用 JSON 扩展。请参阅 JSON 扩展。
生成 JSON
要生成包含 JSON 数据的 Ruby 字符串,请使用方法 JSON.generate(source, opts),其中
-
source是一个 Ruby 对象。 -
opts是一个 Hash 对象,包含控制输入允许和输出格式的选项。
从数组生成 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 Hash 时,JSON.generate 返回一个包含 JSON 对象的字符串
ruby = {foo: 0, bar: 's', baz: :bat} json = JSON.generate(ruby) json # => '{"foo":0,"bar":"s","baz":"bat"}'
Ruby Hash 可以包含嵌套的数组、哈希和标量,深度任意
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"'
当源是 true、false 或 nil 时,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 (boolean) 指定是否允许生成 NaN、Infinity 和 -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]'
选项 allow_duplicate_key (boolean) 指定是否允许带有重复键的哈希或产生错误。默认为发出弃用警告。
默认情况下(未设置)
Warning[:deprecated] = true JSON.generate({ foo: 1, "foo" => 2 }) # warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}. # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true` # => '{"foo":1,"foo":2}'
设置为 false 时
JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false) # detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
在 3.0 版本中,false 将成为默认值。
选项 max_nesting (Integer) 指定 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 (boolean) 指定是否应转义 '\u2028'、'\u2029' 和 '/',以便 JSON 对象可以安全地插入到 script 标签中。
选项 ascii_only (boolean) 指定是否应转义所有不在 ASCII 范围内的字符。
输出选项
默认格式选项生成最紧凑的 JSON 数据,全部在一行上且没有空格。
您可以使用这些格式选项来生成更开放格式的 JSON 数据,使用空格。另请参阅 JSON.pretty_generate。
-
选项
array_nl(String) 指定在每个 JSON 数组后插入的字符串(通常是换行符);默认为空字符串''。 -
选项
object_nl(String) 指定在每个 JSON 对象后插入的字符串(通常是换行符);默认为空字符串''。 -
选项
indent(String) 指定用于缩进的字符串(通常是空格);默认为空字符串'';没有效果,除非选项array_nl或object_nl指定了换行符。 -
选项
space(String) 指定在每个 JSON 对象键值对的冒号后插入的字符串(通常是空格);默认为空字符串''。 -
选项
space_before(String) 指定在每个 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 扩展
注意: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 字符串中。
-
JSON.parse,使用选项
create_additions调用时,会使用该信息来创建正确的 Ruby 对象。
此示例显示 Range 如何生成到 JSON 并解析回 Ruby,分别在没有和有 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 = <<~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 扩展外,您还可以为 Ruby 内置类或用户定义的类创建自己的 JSON 扩展。
这是一个用户定义的类 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)
Constants
属性
返回 JSON 使用的 JSON 生成器模块。
返回 JSON 使用的 JSON 解析器类。
设置或返回 JSON 使用的 JSON 生成器状态类。
Public Class Methods
Source
# File ext/json/lib/json/common.rb, line 132 def [](object, opts = nil) 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
如果 object 是字符串,则调用 JSON.parse 并传入 object 和 opts(参见方法 parse)。
json = '[0, 1, null]' JSON[json]# => [0, 1, nil]
否则,调用 JSON.generate 并传入 object 和 opts(参见方法 generate)。
ruby = [0, 1, nil] JSON[ruby] # => '[0,1,null]'
Source
# File ext/json/lib/json/common.rb, line 234 def self.create_id Thread.current[:"JSON.create_id"] || 'json_class' end
返回当前创建标识符。另请参阅 JSON.create_id=。
Source
# File ext/json/lib/json/common.rb, line 228 def self.create_id=(new_value) Thread.current[:"JSON.create_id"] = new_value.dup.freeze end
设置创建标识符,用于确定是否应调用类的 json_create 钩子;初始值为 json_class。
JSON.create_id # => 'json_class'
私有类方法
Source
# File ext/json/lib/json/common.rb, line 203 def deprecated_singleton_attr_accessor(*attrs) args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : "" attrs.each do |attr| singleton_class.class_eval <<~RUBY def #{attr} warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args} @#{attr} end def #{attr}=(val) warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args} @#{attr} = val end def _#{attr} @#{attr} end RUBY end end
Source
# File ext/json/lib/json/common.rb, line 185 def on_mixed_keys_hash(hash, do_raise) set = {} hash.each_key do |key| key_str = key.to_s if set[key_str] message = "detected duplicate key #{key_str.inspect} in #{hash.inspect}" if do_raise raise GeneratorError, message else deprecation_warning("#{message}.\nThis will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`") end else set[key_str] = true end end end
当哈希同时具有字符串和符号键时,由扩展调用。
Public Instance Methods
Source
# File ext/json/lib/json/common.rb, line 930 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 = opts.merge(kwargs) if kwargs begin State.generate(obj, opts, anIO) rescue JSON::NestingError raise ArgumentError, "exceed depth limit" end end
将 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"}
Source
# File ext/json/lib/json/common.rb, line 460 def fast_generate(obj, opts = nil) if RUBY_VERSION >= "3.0" warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated else warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1 end generate(obj, opts) end
这里的参数 obj 和 opts 与 JSON.generate 中的参数 obj 和 opts 相同。
默认情况下,生成 JSON 数据而不检查 obj 中的循环引用(选项 max_nesting 设置为 false,已禁用)。
如果 obj 包含循环引用,则抛出异常。
a = []; b = []; a.push(b); b.push(a) # Raises SystemStackError (stack level too deep): JSON.fast_generate(a)
Source
# File ext/json/lib/json/common.rb, line 439 def generate(obj, opts = nil) if State === opts opts.generate(obj) else State.generate(obj, opts, nil) end end
返回一个包含生成的 JSON 数据的字符串。
另请参阅 JSON.pretty_generate。
参数 obj 是要转换为 JSON 的 Ruby 对象。
参数 opts,如果给定,包含一个用于生成选项的 Hash。请参阅 生成选项。
当 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)
Source
# File ext/json/lib/json/common.rb, line 854 def load(source, proc = nil, options = nil) if proc && options.nil? && proc.is_a?(Hash) options = proc proc = nil end opts = if options.nil? if proc && proc.is_a?(Hash) options, proc = proc, nil options else _load_default_options end 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 if proc opts = opts.dup opts[:on_load] = proc.to_proc end parse(source, opts) end
返回通过解析给定 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,如果给定,包含一个用于解析选项的 Hash。请参阅 解析选项。默认选项可以通过方法 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": "jane@example.com"}, {"type": "User", "username": "john", "email": "john@example.com"} ], "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 obj }) pp ruby
输出
{"users"=>
[#<User:0x00000000064c4c98
@attributes=
{"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
#<User:0x00000000064c4bd0
@attributes=
{"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
"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"}>}
Source
# File ext/json/lib/json/common.rb, line 388 def load_file(filespec, opts = nil) parse(File.read(filespec, encoding: Encoding::UTF_8), opts) end
Source
# File ext/json/lib/json/common.rb, line 399 def load_file!(filespec, opts = nil) parse!(File.read(filespec, encoding: Encoding::UTF_8), opts) end
Source
# File ext/json/lib/json/common.rb, line 351 def parse(source, opts = nil) opts = ParserOptions.prepare(opts) unless opts.nil? Parser.parse(source, opts) end
返回通过解析给定 source 创建的 Ruby 对象。
参数 source 包含要解析的字符串。
参数 opts,如果给定,包含一个用于解析选项的 Hash。请参阅 解析选项。
当 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 Hash。
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('')
Source
# File ext/json/lib/json/common.rb, line 373 def parse!(source, opts = nil) if opts.nil? parse(source, PARSE_L_OPTIONS) else parse(source, PARSE_L_OPTIONS.merge(opts)) end end
调用
parse(source, opts)
使用 source 和可能已修改的 opts。
与 JSON.parse 的区别
-
选项
max_nesting,如果未提供,则默认为false,这会禁用嵌套深度检查。 -
选项
allow_nan,如果未提供,则默认为true。
Source
# File ext/json/lib/json/common.rb, line 507 def pretty_generate(obj, opts = nil) return opts.generate(obj) if State === opts options = PRETTY_GENERATE_OPTIONS if opts unless opts.is_a?(Hash) 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 end options = options.merge(opts) end State.generate(obj, options, nil) end
这里的参数 obj 和 opts 与 JSON.generate 中的参数 obj 和 opts 相同。
默认选项为
{
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
}
}
Source
# File ext/json/lib/json/common.rb, line 683 def unsafe_load(source, proc = nil, options = nil) opts = if options.nil? if proc && proc.is_a?(Hash) options, proc = proc, nil options else _unsafe_load_default_options end 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 if proc opts = opts.dup opts[:on_load] = proc.to_proc end parse(source, opts) end
返回通过解析给定 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,如果给定,包含一个用于解析选项的 Hash。请参阅 解析选项。默认选项可以通过方法 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": "jane@example.com"}, {"type": "User", "username": "john", "email": "john@example.com"} ], "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 obj }) pp ruby
输出
{"users"=>
[#<User:0x00000000064c4c98
@attributes=
{"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
#<User:0x00000000064c4bd0
@attributes=
{"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
"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"}>}