ChatGPT解决这个技术问题 Extra ChatGPT

Parsing a JSON string in Ruby

I have a string that I want to parse in Ruby:

string = '{"desc":{"someKey":"someValue","anotherKey":"value"},"main_item":{"stats":{"a":8,"b":12,"c":10}}}'

Is there an easy way to extract the data?

JSON is directly supported in Ruby, and has been since at least Ruby v1.9.3, so there is no need to install a gem unless you're using something older. Simply use require 'json' in your code.

A
Andrew Marshall

This looks like JavaScript Object Notation (JSON). You can parse JSON that resides in some variable, e.g. json_string, like so:

require 'json'
JSON.parse(json_string)

If you’re using an older Ruby, you may need to install the json gem.

There are also other implementations of JSON for Ruby that may fit some use-cases better:

YAJL C Bindings for Ruby

JSON::Stream


Also you can sets the option symbolize_names to true, in order to get keys as symbols. Exemple: JSON.parse(string, symbolize_names: true) #=> {key: :value}
JSON is directly supported in Ruby, and has been since at least Ruby v1.9.3, so there is no need to install a gem unless you're using something older. Simply use require 'json' in your code.
n
nevan king

Just to extend the answers a bit with what to do with the parsed object:

# JSON Parsing example
require "rubygems" # don't need this if you're Ruby v1.9.3 or higher
require "json"

string = '{"desc":{"someKey":"someValue","anotherKey":"value"},"main_item":{"stats":{"a":8,"b":12,"c":10}}}'
parsed = JSON.parse(string) # returns a hash

p parsed["desc"]["someKey"]
p parsed["main_item"]["stats"]["a"]

# Read JSON from a file, iterate over objects
file = open("shops.json")
json = file.read

parsed = JSON.parse(json)

parsed["shop"].each do |shop|
  p shop["id"]
end

Important note: '{ "a": "bob" }' is valid. "{ 'a': 'bob' }" is not.
@LinusAn because JSON requires double quotes around strings. See string in the JSON definition ( json.org ): "A string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes."
In many cases you want to wrap JSON.parse within a rescue block for JSON::ParserError.
JSON.parse("[#{value}]")[0] to avoid the error A JSON text must at least contain two octets!
Can you make your answer complete by including a sample example data from shops.json?
t
the Tin Man

As of Ruby v1.9.3 you don't need to install any Gems in order to parse JSON, simply use require 'json':

require 'json'

json = JSON.parse '{"foo":"bar", "ping":"pong"}'
puts json['foo'] # prints "bar"

See JSON at Ruby-Doc.


t
the Tin Man

It looks like a JSON string. You can use one of many JSON libraries and it's as simple as doing:

JSON.parse(string)

t
the Tin Man

This is a bit late but I ran into something interesting that seems important to contribute.

I accidentally wrote this code, and it seems to work:

require 'yaml'
CONFIG_FILE = ENV['CONFIG_FILE'] # path to a JSON config file 
configs = YAML.load_file("#{CONFIG_FILE}")
puts configs['desc']['someKey']

I was surprised to see it works since I am using the YAML library, but it works.

The reason why it is important is that yaml comes built-in with Ruby so there's no gem install.

I am using versions 1.8.x and 1.9.x - so the json library is not built in, but it is in version 2.x.

So technically - this is the easiest way to extract the data in version lower than 2.0.


Yes, JSON is actually parsed by the Psych code, which also parses YAML in Ruby. And JSON parsing was introduced in Ruby v1.9.3.
The reason this works is that semantically (most) JSON is valid YAML (particularly YAML 1.2)
A
Adverbly

Don't see any answers here that mention parsing directly to an object other than a Hash, but it is possible using the poorly-documented object_class option(see https://ruby-doc.org/stdlib-2.7.1/libdoc/json/rdoc/JSON.html):

JSON.parse('{"foo":{"bar": 2}}', object_class: OpenStruct).foo.bar
=> 2

The better way to read that option is "The ruby class that a json object turns into", which explains why it defaults to Hash. Likewise, there is an array_class option for json arrays.


m
mycargus

I suggest Oj as it is waaaaaay faster than the standard JSON library.

https://github.com/ohler55/oj

(see performance comparisons here)


E
Erik Madsen

If you want to deserialise to your own class instead of OpenStruct it doesn't take a lot of work to make the following possible:

require 'json'
# result is an instance of MyClass
result = JSON.parse(some_json_string, object_class: MyClass)

All you have to do is to provide a zero-argument constructor and implement the #[]= method which JSON.parse will call. If you don't want to expose it, it's sufficient to let it be private:

class MyClass
  attr_reader :a, :b

  private

  def []=(key, value)
    case key
    when 'a' then @a = value
    when 'b' then @b = value
    end
  end
end

Trying it out in irb:

> JSON.parse('{"a":1, "b":2}', object_class: MyClass)
=> #<MyClass:0x00007fe00913ae98 @a=1, @b=2>

A caveat with this approach is that it only works for flat structures, because the object_class argument really tells the parser what class it should use to deserialise JSON objects in the string instead of Hash (see the similar argument array_class for the analogous operation for JSON arrays). For nested structures this will mean you will use the same class to represent all layers:

> JSON.parse('{"a":1, "b":{ "a": 32 }}', object_class: MyClass)
=> #<MyClass:0x00007fb5110b2b38 @a=1, @b=#<MyClass:0x00007fb5110b2908 @a=32>>