модуль 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]]
Использование модуля JSON
Чтобы модуль JSON был доступен в вашем коде, начните с:
require 'json'
Во всех примерах предполагается, что это сделано.
Парсинг JSON
Вы можете проанализировать строку, содержащую данные JSON, используя один из двух методов:
-
JSON.parse(source, opts) -
JSON.parse!(source, opts)
где
-
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:
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 (булево) указывает, разрешать ли NaN, Infinity и MinusInfinity в source; по умолчанию 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 (булево) указывает, должны ли ключи возвращаемого Hash быть символами; по умолчанию 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 (класс) указывает класс Ruby, который будет использоваться для каждого JSON-объекта; по умолчанию 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 (класс) указывает класс Ruby, который будет использоваться для каждого JSON-массива; по умолчанию 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 (булево) указывает, использовать ли дополнения JSON при парсинге. См. Дополнения JSON.
Генерация JSON
Для создания строки Ruby, содержащей данные JSON, используйте метод 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, 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"'
Если исходный код — 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 (булево) указывает, можно ли генерировать 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]'
Опция 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)
Опции вывода
Параметры форматирования по умолчанию генерируют наиболее компактные данные JSON, все на одной строке без пробелов.
Вы можете использовать эти параметры форматирования для генерации данных JSON в более открытом формате с использованием пробелов. См. также JSON.pretty_generate.
-
Опция
array_nl(строка) задаёт строку (обычно перевод строки), которая вставляется после каждого JSON-массива; по умолчанию пустая строка,''. -
Опция
object_nl(строка) задаёт строку (обычно перевод строки), которая вставляется после каждого JSON-объекта; по умолчанию пустая строка,''. -
Опция
indent(строка) задаёт строку (обычно пробелы) для отступов; по умолчанию пустая строка,''; по умолчанию пустая строка,''; не имеет эффекта, если опцииarray_nlилиobject_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.
-
JSON.parse, вызываемый с опцией
create_additions, использует эту информацию для создания надлежащего объекта Ruby.
В этом примере показано, как диапазон генерируется в JSON и анализируется обратно в 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'
Для сокращения количества знаков препинания примеры ниже показывают сгенерированный JSON через puts, а не обычный 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
Диапазон:
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
Рациональное число:
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
Набор:
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
Структура:
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
Символ:
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
Время:
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) Константы
- CREATE_ID_TLS_KEY
- DEFAULT_CREATE_ID
- Infinity
- JSON_LOADED
- MinusInfinity
- NaN
- VERSION
-
JSONверсия
Атрибуты
Устанавливает или возвращает параметры по умолчанию для метода JSON.dump. Изначально:
opts = JSON.dump_default_options
opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
Устанавливает или возвращает параметры по умолчанию для метода JSON.load. Изначально:
opts = JSON.load_default_options
opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
Общедоступные методы класса
# File ext/json/lib/json/common.rb, line 18
def [](object, opts = {})
if object.respond_to? :to_str
JSON.parse(object.to_str, opts)
else
JSON.generate(object, opts)
end
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]'
# File ext/json/lib/json/common.rb, line 81
def create_fast_state
State.new(
:indent => '',
:space => '',
:object_nl => "",
:array_nl => "",
:max_nesting => false
)
end # File ext/json/lib/json/common.rb, line 126 def self.create_id Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID end
Возвращает текущий идентификатор создания. См. также JSON.create_id=.
# File ext/json/lib/json/common.rb, line 120 def self.create_id=(new_value) Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze end
Устанавливает идентификатор создания, который используется для определения, следует ли вызывать обработчик json_create класса; начальное значение — json_class:
JSON.create_id # => 'json_class'
# File ext/json/lib/json/common.rb, line 91
def create_pretty_state
State.new(
:indent => ' ',
:space => ' ',
:object_nl => "\n",
:array_nl => "\n"
)
end # File ext/json/lib/json/common.rb, line 653 def self.iconv(to, from, string) string.encode(to, from) end
Кодирует строку с помощью String.encode.
Методы публичного экземпляра
# File ext/json/lib/json/common.rb, line 631
def dump(obj, anIO = nil, limit = nil)
if anIO and limit.nil?
anIO = anIO.to_io if anIO.respond_to?(:to_io)
unless anIO.respond_to?(:write)
limit = anIO
anIO = nil
end
end
opts = JSON.dump_default_options
opts = opts.merge(:max_nesting => limit) if limit
result = generate(obj, opts)
if anIO
anIO.write result
anIO
else
result
end
rescue JSON::NestingError
raise ArgumentError, "exceed depth limit"
end Выводит obj в виде строки JSON, т.е. вызывает generate для объекта и возвращает результат.
Значения по умолчанию можно изменить с помощью метода JSON.dump_default_options.
-
Аргумент
io, если задан, должен отвечать на методwrite; строка JSON записывается вio, и возвращаетсяio. Еслиioне задан, возвращается строка JSON. -
Аргумент
limit, если задан, передается методуJSON.generateв качестве параметраmax_nesting.
Когда аргумент io не задан, возвращает строку JSON, сгенерированную из obj.
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 ext/json/lib/json/common.rb, line 335
def fast_generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
else
state = JSON.create_fast_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 Аргументы obj и opts здесь такие же, как аргументы obj и opts в методе JSON.generate.
По умолчанию генерирует данные 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 ext/json/lib/json/common.rb, line 296
def generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
else
state = State.new
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 = state.configure(opts)
end
state.generate(obj)
end Возвращает строку, содержащую сгенерированные данные JSON.
См. также JSON.fast_generate, JSON.pretty_generate.
Аргумент obj — объект Ruby, который нужно преобразовать в JSON.
Аргумент 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 ext/json/lib/json/common.rb, line 557
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 Возвращает объекты Ruby, созданные путём парсинга заданного source.
-
Аргумент
sourceдолжен быть или преобразовываться в строку:-
Если
sourceотвечает на метод экземпляраto_str,source.to_strстановится источником. -
Если
sourceотвечает на метод экземпляраto_io,source.to_io.readстановится источником. -
Если
sourceотвечает на метод экземпляраread,source.readстановится источником. -
Если оба следующих условия верны, источник становится строкой
'null':-
Параметр
allow_blankзадаёт истинное значение. -
Источник, как определено выше, является
nilили пустой строкой''.
-
-
В противном случае
sourceостаётся источником.
-
-
Аргумент
proc, если задан, должен быть процедурой, принимающей один аргумент. Она будет вызываться рекурсивно с каждым результатом (порядок обхода в глубину). ВНИМАНИЕ: Этот метод предназначен для сериализации данных из надёжных источников пользовательского ввода, например, с вашего собственного сервера базы данных или клиентов под вашим контролем. Опасно разрешать ненадежным пользователям передавать в него источникиJSON. -
Аргумент
opts, если задан, содержит словарь параметров для парсинга. См. Параметры парсинга. Значения по умолчанию можно изменить с помощью методаJSON.load_default_options=.
Если proc не задан, он изменяет source как указано выше и возвращает результат вызова parse(source, opts); см. parse.
Источник для следующих примеров:
source = <<-EOT
{
"name": "Dave",
"age" :40,
"hats": [
"Cattleman's",
"Panama",
"Tophat"
]
}
EOT
Загрузка строки:
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"]}
Загрузка объекта File:
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как указано выше. -
Получает значение
result, вызвавparse(source, opts). -
Рекурсивно вызывает
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
})
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"}>} # File ext/json/lib/json/common.rb, line 245
def load_file(filespec, opts = {})
parse(File.read(filespec), opts)
end Вызывает:
parse(File.read(path), opts)
См. метод parse.
# File ext/json/lib/json/common.rb, line 256
def load_file!(filespec, opts = {})
parse!(File.read(filespec), opts)
end Вызывает:
JSON.parse!(File.read(path, opts))
См. метод parse!
# File ext/json/lib/json/common.rb, line 215
def parse(source, opts = {})
Parser.new(source, **(opts||{})).parse
end Возвращает объекты Ruby, созданные путём парсинга заданного source.
Аргумент 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 = <<-EOT
{
"name": "Dave",
"age" :40,
"hats": [
"Cattleman's",
"Panama",
"Tophat"
]
}
EOT
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 ext/json/lib/json/common.rb, line 230
def parse!(source, opts = {})
opts = {
:max_nesting => false,
:allow_nan => true
}.merge(opts)
Parser.new(source, **(opts||{})).parse
end Вызывает
parse(source, opts)
с source и, возможно, изменённым opts.
Отличия от JSON.parse:
-
Параметр
max_nesting, если не указан, по умолчанию равенfalse, что отключает проверку глубины вложенности. -
Параметр
allow_nan, если не указан, по умолчанию равенtrue.
# File ext/json/lib/json/common.rb, line 390
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 Аргументы obj и opts здесь такие же, как аргументы obj и opts в методе JSON.generate.
Параметры по умолчанию:
{
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
}
}
Методы приватного экземпляра
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.