Modul JSON

JavaScript Object Notation (JSON)

JSON ist ein leichtgewichtiges Datenformat.

Ein JSON-Wert ist einer der folgenden:

Ein JSON-Array oder -Objekt kann verschachtelte Arrays, Objekte und Skalare bis zu jeder Tiefe enthalten.

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

Verwendung des Moduls JSON

Um das Modul JSON in Ihrem Code verfügbar zu machen, beginnen Sie mit

require 'json'

Alle Beispiele hier gehen davon aus, dass dies geschehen ist.

JSON parsen

Sie können einen String mit JSON-Daten mit einer der beiden Methoden parsen:

wobei

Der Unterschied zwischen den beiden Methoden besteht darin, dass JSON.parse! einige Prüfungen weglässt und für einige source-Daten möglicherweise nicht sicher ist; verwenden Sie sie nur für Daten aus vertrauenswürdigen Quellen. Verwenden Sie die sicherere Methode JSON.parse für weniger vertrauenswürdige Quellen.

JSON-Arrays parsen

Wenn source ein JSON-Array ist, gibt JSON.parse standardmäßig ein Ruby-Array zurück.

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

Das JSON-Array kann verschachtelte Arrays, Objekte und Skalare bis zu jeder Tiefe enthalten.

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

JSON-Objekte parsen

Wenn die Quelle ein JSON-Objekt ist, gibt JSON.parse standardmäßig einen Ruby-Hash zurück.

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

Das JSON-Objekt kann verschachtelte Arrays, Objekte und Skalare bis zu jeder Tiefe enthalten.

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

JSON-Skalare parsen

Wenn die Quelle ein JSON-Skalar ist (kein Array oder Objekt), gibt JSON.parse einen Ruby-Skalar zurück.

String

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

Integer

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

Float

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

Boolean

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

Parsing-Optionen

Eingabeoptionen

Die Option max_nesting (Integer) gibt die maximal erlaubte Verschachtelungstiefe an; Standardwert ist 100; geben Sie false an, um die Tiefenprüfung zu deaktivieren.

Mit dem Standardwert false

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

Zu tief

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

Ungültiger Wert

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

Die Option allow_duplicate_key gibt an, ob doppelte Schlüssel in Objekten ignoriert oder einen Fehler auslösen sollen.

Wenn nicht angegeben

# 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`

Wenn auf 'true' gesetzt

# The last value is used.
JSON.parse('{"a": 1, "a":2}') => {"a" => 2}

Wenn auf 'false' gesetzt, der zukünftige Standardwert

JSON.parse('{"a": 1, "a":2}') => duplicate key at line 1 column 1 (JSON::ParserError)

Die Option allow_nan (Boolean) gibt an, ob NaN, Infinity und MinusInfinity in source erlaubt sind; Standardwert ist false.

Mit dem Standardwert 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]')

Erlauben

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

Die Option allow_trailing_comma (Boolean) gibt an, ob nachgestellte Kommas in Objekten und Arrays erlaubt sind; Standardwert ist false.

Mit dem Standardwert false

JSON.parse('[1,]') # unexpected character: ']' at line 1 column 4 (JSON::ParserError)

Wenn aktiviert

JSON.parse('[1,]', allow_trailing_comma: true) # => [1]
Ausgabeoptionen

Die Option freeze (Boolean) gibt an, ob die zurückgegebenen Objekte "eingefroren" (unveränderlich) sein sollen; Standardwert ist false.

Die Option symbolize_names (Boolean) gibt an, ob die Schlüssel der zurückgegebenen Hashes Symbole sein sollen; Standardwert ist false (verwende Strings).

Mit dem Standardwert 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}

Symbole verwenden

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

Die Option object_class (Class) gibt die Ruby-Klasse an, die für jedes JSON-Objekt verwendet werden soll; Standardwert ist Hash.

Mit dem Standardwert, Hash

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

Klasse OpenStruct verwenden

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

Die Option array_class (Class) gibt die Ruby-Klasse an, die für jedes JSON-Array verwendet werden soll; Standardwert ist Array.

Mit dem Standardwert, Array

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

Klasse Set verwenden

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

Die Option create_additions (Boolean) gibt an, ob JSON-Erweiterungen beim Parsen verwendet werden sollen. Siehe JSON-Erweiterungen.

JSON generieren

Um einen Ruby-String mit JSON-Daten zu generieren, verwenden Sie die Methode JSON.generate(source, opts), wobei

JSON aus Arrays generieren

Wenn die Quelle ein Ruby-Array ist, gibt JSON.generate einen String mit einem JSON-Array zurück.

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

Das Ruby-Array kann verschachtelte Arrays, Hashes und Skalare bis zu jeder Tiefe enthalten.

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

JSON aus Hashes generieren

Wenn die Quelle ein Ruby-Hash ist, gibt JSON.generate einen String mit einem JSON-Objekt zurück.

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

Das Ruby-Hash-Array kann verschachtelte Arrays, Hashes und Skalare bis zu jeder Tiefe enthalten.

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 aus anderen Objekten generieren

Wenn die Quelle weder ein Array noch ein Hash ist, hängen die generierten JSON-Daten von der Klasse der Quelle ab.

Wenn die Quelle eine Ruby-Ganzzahl oder -Gleitkommazahl ist, gibt JSON.generate einen String mit einer JSON-Zahl zurück.

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

Wenn die Quelle ein Ruby-String ist, gibt JSON.generate einen String mit einem JSON-String (mit doppelten Anführungszeichen) zurück.

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

Wenn die Quelle true, false oder nil ist, gibt JSON.generate einen String mit dem entsprechenden JSON-Token zurück.

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

Wenn die Quelle keine der obigen ist, gibt JSON.generate einen String mit einer JSON-String-Repräsentation der Quelle zurück.

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

Generierungsoptionen

Eingabeoptionen

Die Option allow_nan (Boolean) gibt an, ob NaN, Infinity und -Infinity generiert werden dürfen; Standardwert ist false.

Mit dem Standardwert 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)

Erlauben

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

Die Option allow_duplicate_key (Boolean) gibt an, ob Hashes mit doppelten Schlüsseln erlaubt sein sollen oder einen Fehler erzeugen. Standard ist die Ausgabe einer Deprecation-Warnung.

Mit dem Standardwert (nicht gesetzt)

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

Mit false

JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
# detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)

In Version 3.0 wird false zum Standard.


Die Option max_nesting (Integer) gibt die maximale Verschachtelungstiefe in obj an; Standardwert ist 100.

Mit dem Standardwert, 100

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

Zu tief

# Raises JSON::NestingError (nesting of 2 is too deep):
JSON.generate(obj, max_nesting: 2)
Escaping-Optionen

Die Optionen script_safe (Boolean) geben an, ob '\u2028', '\u2029' und '/' so maskiert werden sollen, dass das JSON-Objekt sicher in Skript-Tags eingefügt werden kann.

Die Option ascii_only (Boolean) gibt an, ob alle Zeichen außerhalb des ASCII-Bereichs maskiert werden sollen.

Ausgabeoptionen

Die Standard-Formatierungsoptionen erzeugen die kompaktesten JSON-Daten, alles auf einer Zeile und ohne Leerzeichen.

Sie können diese Formatierungsoptionen verwenden, um JSON-Daten in einem offeneren Format mit Leerzeichen zu generieren. Siehe auch JSON.pretty_generate.

In diesem Beispiel wird obj zuerst verwendet, um die kürzesten JSON-Daten (ohne Leerzeichen) zu generieren, und dann erneut mit allen angegebenen Formatierungsoptionen.

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)

Ausgabe

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

JSON-Erweiterungen

Beachten Sie, dass JSON-Erweiterungen nur mit vertrauenswürdigen Daten verwendet werden dürfen und veraltet sind.

Wenn Sie ein Nicht-String-Objekt von Ruby nach JSON und zurück "roundtrippen", erhalten Sie einen neuen String anstelle des ursprünglichen Objekts.

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

Sie können JSON-Erweiterungen verwenden, um das ursprüngliche Objekt zu erhalten. Die Erweiterung ist eine Erweiterung einer Ruby-Klasse, sodass

Dieses Beispiel zeigt einen Range, der in JSON generiert und zurück nach Ruby geparst wird, sowohl ohne als auch mit der Erweiterung für 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

Diese Ausgabe zeigt die unterschiedlichen Ergebnisse.

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)

Das JSON-Modul enthält Erweiterungen für bestimmte Klassen. Sie können auch benutzerdefinierte Erweiterungen erstellen. Siehe Benutzerdefinierte JSON-Erweiterungen.

Integrierte Erweiterungen

Das JSON-Modul enthält Erweiterungen für bestimmte Klassen. Um eine Erweiterung zu verwenden, requiren Sie ihre Quelle.

Um die Zeichensetzung zu reduzieren, zeigen die folgenden Beispiele das generierte JSON über puts anstelle des üblichen 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 (und seine Unterklassen, einschließlich 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

Benutzerdefinierte JSON-Erweiterungen

Zusätzlich zu den bereitgestellten JSON-Erweiterungen können Sie eigene JSON-Erweiterungen erstellen, entweder für integrierte Ruby-Klassen oder für benutzerdefinierte Klassen.

Hier ist eine benutzerdefinierte Klasse Foo.

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

Hier ist die JSON-Erweiterung dafür.

# 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

Demonstration

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

Ausgabe

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)