This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
# JSON implementation for Ruby
|
||||
|
||||
[](https://github.com/ruby/json/actions/workflows/ci.yml)
|
||||
|
||||
## Description
|
||||
|
||||
This is an implementation of the JSON specification according to RFC 7159
|
||||
http://www.ietf.org/rfc/rfc7159.txt .
|
||||
|
||||
The JSON generator generate UTF-8 character sequences by default.
|
||||
If an :ascii\_only option with a true value is given, they escape all
|
||||
non-ASCII and control characters with \uXXXX escape sequences, and support
|
||||
UTF-16 surrogate pairs in order to be able to generate the whole range of
|
||||
unicode code points.
|
||||
|
||||
All strings, that are to be encoded as JSON strings, should be UTF-8 byte
|
||||
sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8
|
||||
encoded, please use the to\_json\_raw\_object method of String (which produces
|
||||
an object, that contains a byte array) and decode the result on the receiving
|
||||
endpoint.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the gem and add to the application's Gemfile by executing:
|
||||
|
||||
$ bundle add json
|
||||
|
||||
If bundler is not being used to manage dependencies, install the gem by executing:
|
||||
|
||||
$ gem install json
|
||||
|
||||
## Basic Usage
|
||||
|
||||
To use JSON you can
|
||||
|
||||
```ruby
|
||||
require 'json'
|
||||
```
|
||||
|
||||
Now you can parse a JSON document into a ruby data structure by calling
|
||||
|
||||
```ruby
|
||||
JSON.parse(document)
|
||||
```
|
||||
|
||||
If you want to generate a JSON document from a ruby data structure call
|
||||
```ruby
|
||||
JSON.generate(data)
|
||||
```
|
||||
|
||||
You can also use the `pretty_generate` method (which formats the output more
|
||||
verbosely and nicely) or `fast_generate` (which doesn't do any of the security
|
||||
checks generate performs, e. g. nesting deepness checks).
|
||||
|
||||
## Casting non native types
|
||||
|
||||
JSON documents can only support Hashes, Arrays, Strings, Integers and Floats.
|
||||
|
||||
By default if you attempt to serialize something else, `JSON.generate` will
|
||||
search for a `#to_json` method on that object:
|
||||
|
||||
```ruby
|
||||
Position = Struct.new(:latitude, :longitude) do
|
||||
def to_json(state = nil, *)
|
||||
JSON::State.from_state(state).generate({
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
JSON.generate([
|
||||
Position.new(12323.234, 435345.233),
|
||||
Position.new(23434.676, 159435.324),
|
||||
]) # => [{"latitude":12323.234,"longitude":435345.233},{"latitude":23434.676,"longitude":159435.324}]
|
||||
```
|
||||
|
||||
If a `#to_json` method isn't defined on the object, `JSON.generate` will fallback to call `#to_s`:
|
||||
|
||||
```ruby
|
||||
JSON.generate(Object.new) # => "#<Object:0x000000011e768b98>"
|
||||
```
|
||||
|
||||
Both of these behavior can be disabled using the `strict: true` option:
|
||||
|
||||
```ruby
|
||||
JSON.generate(Object.new, strict: true) # => Object not allowed in JSON (JSON::GeneratorError)
|
||||
JSON.generate(Position.new(1, 2)) # => Position not allowed in JSON (JSON::GeneratorError)
|
||||
```
|
||||
|
||||
## JSON::Coder
|
||||
|
||||
Since `#to_json` methods are global, it can sometimes be problematic if you need a given type to be
|
||||
serialized in different ways in different locations.
|
||||
|
||||
Instead it is recommended to use the newer `JSON::Coder` API:
|
||||
|
||||
```ruby
|
||||
module MyApp
|
||||
API_JSON_CODER = JSON::Coder.new do |object|
|
||||
case object
|
||||
when Time
|
||||
object.iso8601(3)
|
||||
else
|
||||
object
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
|
||||
```
|
||||
|
||||
The provided block is called for all objects that don't have a native JSON equivalent, and
|
||||
must return a Ruby object that has a native JSON equivalent.
|
||||
|
||||
## Combining JSON fragments
|
||||
|
||||
To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`:
|
||||
|
||||
```ruby
|
||||
posts_json = cache.fetch_multi(post_ids) do |post_id|
|
||||
JSON.generate(Post.find(post_id))
|
||||
end
|
||||
posts_json.map! { |post_json| JSON::Fragment.new(post_json) }
|
||||
JSON.generate({ posts: posts_json, count: posts_json.count })
|
||||
```
|
||||
|
||||
## Round-tripping arbitrary types
|
||||
|
||||
> [!CAUTION]
|
||||
> You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input,
|
||||
> as it can lead to remote code execution vulnerabilities.
|
||||
|
||||
To create a JSON document from a ruby data structure, you can call
|
||||
`JSON.generate` like that:
|
||||
|
||||
```ruby
|
||||
json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
||||
# => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
|
||||
```
|
||||
|
||||
To get back a ruby data structure from a JSON document, you have to call
|
||||
JSON.parse on it:
|
||||
|
||||
```ruby
|
||||
JSON.parse json
|
||||
# => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
|
||||
```
|
||||
|
||||
Note, that the range from the original data structure is a simple
|
||||
string now. The reason for this is, that JSON doesn't support ranges
|
||||
or arbitrary classes. In this case the json library falls back to call
|
||||
`Object#to_json`, which is the same as `#to_s.to_json`.
|
||||
|
||||
It's possible to add JSON support serialization to arbitrary classes by
|
||||
simply implementing a more specialized version of the `#to_json method`, that
|
||||
should return a JSON object (a hash converted to JSON with `#to_json`) like
|
||||
this (don't forget the `*a` for all the arguments):
|
||||
|
||||
```ruby
|
||||
class Range
|
||||
def to_json(*a)
|
||||
{
|
||||
'json_class' => self.class.name, # = 'Range'
|
||||
'data' => [ first, last, exclude_end? ]
|
||||
}.to_json(*a)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The hash key `json_class` is the class, that will be asked to deserialise the
|
||||
JSON representation later. In this case it's `Range`, but any namespace of
|
||||
the form `A::B` or `::A::B` will do. All other keys are arbitrary and can be
|
||||
used to store the necessary data to configure the object to be deserialised.
|
||||
|
||||
If the key `json_class` is found in a JSON object, the JSON parser checks
|
||||
if the given class responds to the `json_create` class method. If so, it is
|
||||
called with the JSON object converted to a Ruby hash. So a range can
|
||||
be deserialised by implementing `Range.json_create` like this:
|
||||
|
||||
```ruby
|
||||
class Range
|
||||
def self.json_create(o)
|
||||
new(*o['data'])
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Now it possible to serialise/deserialise ranges as well:
|
||||
|
||||
```ruby
|
||||
json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
||||
# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
|
||||
JSON.parse json
|
||||
# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
||||
json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
||||
# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
|
||||
JSON.unsafe_load json
|
||||
# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
||||
```
|
||||
|
||||
`JSON.generate` always creates the shortest possible string representation of a
|
||||
ruby data structure in one line. This is good for data storage or network
|
||||
protocols, but not so good for humans to read. Fortunately there's also
|
||||
`JSON.pretty_generate` (or `JSON.pretty_generate`) that creates a more readable
|
||||
output:
|
||||
|
||||
```ruby
|
||||
puts JSON.pretty_generate([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
|
||||
[
|
||||
1,
|
||||
2,
|
||||
{
|
||||
"a": 3.141
|
||||
},
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
{
|
||||
"json_class": "Range",
|
||||
"data": [
|
||||
4,
|
||||
10,
|
||||
false
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
There are also the methods `Kernel#j` for generate, and `Kernel#jj` for
|
||||
`pretty_generate` output to the console, that work analogous to Core Ruby's `p` and
|
||||
the `pp` library's `pp` methods.
|
||||
|
||||
## Development
|
||||
|
||||
### Release
|
||||
|
||||
Update the `lib/json/version.rb` file.
|
||||
|
||||
```
|
||||
rbenv shell 2.6.5
|
||||
rake build
|
||||
gem push pkg/json-2.3.0.gem
|
||||
|
||||
rbenv shell jruby-9.2.9.0
|
||||
rake build
|
||||
gem push pkg/json-2.3.0-java.gem
|
||||
```
|
||||
|
||||
## Author
|
||||
|
||||
Florian Frank <mailto:flori@ping.de>
|
||||
|
||||
## License
|
||||
|
||||
Ruby License, see https://www.ruby-lang.org/en/about/license.txt.
|
||||
|
||||
## Download
|
||||
|
||||
The latest version of this library can be downloaded at
|
||||
|
||||
* https://rubygems.org/gems/json
|
||||
|
||||
Online Documentation should be located at
|
||||
|
||||
* https://www.rubydoc.info/gems/json
|
||||
|
||||
[Ragel]: http://www.colm.net/open-source/ragel/
|
||||
Reference in New Issue
Block a user