модуль JSON
Нотация объектов JavaScript (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
Разобрать String с данными 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 по умолчанию возвращает Hash 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 (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) определяет, разрешены ли 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]
Параметр 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 быть символами; по умолчанию используется false (используются String).
При значении по умолчанию, 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 (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 (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 (boolean) определяет, следует ли использовать дополнения JSON при разборе. См. раздел Дополнения JSON.
Генерация JSON
Чтобы сгенерировать String Ruby с данными JSON, используйте метод JSON.generate(source, opts), где
-
source— объект Ruby. -
opts— объект Hash, содержащий параметры, управляющие как допустимым вводом, так и форматированием вывода.
Генерация JSON из массивов
Если источник — массив Ruby, JSON.generate возвращает String, содержащий массив JSON:
ruby = [0, 's', :foo] json = JSON.generate(ruby) json # => '[0,"s","foo"]'
Массив Ruby array может содержать вложенные массивы, хеши и скалярные значения любой глубины:
ruby = [0, [1, 2], {foo: 3, bar: 4}]
json = JSON.generate(ruby)
json # => '[0,[1,2],{"foo":3,"bar":4}]'
Генерация JSON из хешей
Если источник — Hash Ruby, JSON.generate возвращает String, содержащий объект JSON:
ruby = {foo: 0, bar: 's', baz: :bat}
json = JSON.generate(ruby)
json # => '{"foo":0,"bar":"s","baz":"bat"}'
Хеш Ruby array может содержать вложенные массивы, хеши и скалярные значения любой глубины:
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 зависят от класса источника.
Если источник — Integer или Float Ruby, JSON.generate возвращает String, содержащий число JSON:
JSON.generate(42) # => '42' JSON.generate(0.42) # => '0.42'
Если источник — String Ruby, JSON.generate возвращает String, содержащий строку JSON (в двойных кавычках):
JSON.generate('A string') # => '"A string"'
Если источник — true, false или nil, JSON.generate возвращает String, содержащий соответствующий токен JSON:
JSON.generate(true) # => 'true' JSON.generate(false) # => 'false' JSON.generate(nil) # => 'null'
Если источник не относится ни к одному из перечисленных выше типов, JSON.generate возвращает String, содержащий строковое представление источника в формате 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; по умолчанию используется пустой String,''. -
Параметр
object_nl(String) задаёт строку (обычно символ новой строки), вставляемую после каждого объекта JSON; по умолчанию используется пустой String,''. -
Параметр
indent(String) задаёт строку (обычно пробелы), используемую для отступов; по умолчанию используется пустой String,''; по умолчанию используется пустой String,''; не действует, если параметрыarray_nlилиobject_nlне задают символы новой строки. -
Параметр
space(String) задаёт строку (обычно пробел), вставляемую после двоеточия в каждой паре объекта JSON; по умолчанию используется пустой String,''. -
Параметр
space_before(String) задаёт строку (обычно пробел), вставляемую перед двоеточием в каждой паре объекта JSON; по умолчанию используется пустой String,''.
В этом примере 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'
Чтобы избежать избыточных знаков препинания, в примерах ниже сгенерированный 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
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) Константы
- Fragment
-
FragmentдокументаJSON, который следует включить как есть:fragment = JSON::Fragment.new("[1, 2, 3]") JSON.generate({ count: 3, items: fragments })Это позволяет легко объединять несколько фрагментов
JSON, сохранённых где-либо, без необходимости разбирать их или прибегать к интерполяции строк.Примечание: предоставленная строка не проверяется. Вызывающий код должен убедиться, что строка содержит корректный
JSON. - Infinity
- JSON_LOADED
- MinusInfinity
- NaN
- PARSE_L_OPTIONS
- PRETTY_GENERATE_OPTIONS
- VERSION
Атрибуты
Открытые методы класса
# 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]'
# File ext/json/lib/json/common.rb, line 234 def self.create_id Thread.current[:"JSON.create_id"] || 'json_class' end
Возвращает текущий идентификатор создания. См. также JSON.create_id=.
# 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'
Закрытые методы класса
# 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 # 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 Вызывается из расширения, если хеш содержит ключи и типа String, и типа Symbol
Открытые методы экземпляра
# 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, если указан, передаётся в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 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 здесь совпадают с аргументами 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 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 — это объект 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 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 Возвращает объекты Ruby, созданные при разборе указанного source.
ВНИМАНИЕ: Этот метод предназначен для десериализации данных из доверенного источника, например с собственного сервера базы данных или от контролируемых вами клиентов. Передача в него источников JSON от недоверенных пользователей может быть опасной. Если вам необходимо использовать этот метод, воспользуйтесь вместо него JSON.unsafe_load, чтобы явно обозначить это.
Начиная с версии JSON 2.8.0, `load` выводит предупреждение об устаревании, если десериализуется тип, отличный от встроенного, без явного включения `create_additions`; в версии JSON 3.0 параметр `create_additions` по умолчанию будет отключён для `load`.
-
Аргумент
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"]}
Загрузка объекта 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
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"}>} # 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
# 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
# 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
Возвращает объекты 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 = <<~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 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.
# 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 здесь совпадают с аргументами 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
}
}
# 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 Возвращает объекты Ruby, созданные при разборе указанного source.
ВНИМАНИЕ: Этот метод предназначен для десериализации данных из доверенного источника, например с собственного сервера базы данных или от контролируемых вами клиентов. Передача в него источников 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"]}
Загрузка объекта File:
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, как описано выше. -
Получает
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.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"}>}
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.