Spec-Zone.ru › Ruby 4.0

модуль 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 можно одним из двух способов:

  • 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 по умолчанию возвращает 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

Атрибуты

generator [R]

Возвращает модуль генератора JSON, используемый JSON.

parser [R]

Возвращает класс парсера JSON, используемый JSON.

state [RW]

Задаёт или возвращает класс состояния генератора JSON, используемый JSON.

Открытые методы класса

JSON[object] → new_array or new_string Показать исходный код
# 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]'
create_id () Показать исходный код
# File ext/json/lib/json/common.rb, line 234
def self.create_id
  Thread.current[:"JSON.create_id"] || 'json_class'
end

Возвращает текущий идентификатор создания. См. также JSON.create_id=.

create_id= (new_value) Показать исходный код
# 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'

Закрытые методы класса

deprecated_singleton_attr_accessor (*attrs) Показать исходный код
# 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
on_mixed_keys_hash (hash, do_raise) Показать исходный код
# 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

Открытые методы экземпляра

dump(obj, io = nil, limit = nil) Показать исходный код
# 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"}
fast_generate(obj, opts) → new_string Показать исходный код
# 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)
generate(obj, opts = nil) → new_string Показать исходный код
# 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)
load(source, options = {}) → object Показать исходный код
load(source, proc = nil, options = {}) → object
# 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"}>}
load_file(path, opts={}) → object Показать исходный код
# 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

Вызывает:

parse(File.read(path), opts)

См. метод parse.

load_file!(path, opts = {}) Показать исходный код
# 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

Вызывает:

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

См. метод parse!

parse(source, opts) → object Показать исходный код
# 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('')
parse!(source, opts) → object Показать исходный код
# 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.

pretty_generate(obj, opts = nil) → new_string Показать исходный код
# 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
  }
}
unsafe_load(source, options = {}) → object Показать исходный код
unsafe_load(source, proc = nil, options = {}) → object
# 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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API