mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* lib/json.rb, lib/json/add/{core.rb, rails.rb},
test/json/test_json_rails.rb: additional files of JSON 1.1.2. [ruby-dev:32405] -- M lib/json.rb A lib/json/add A lib/json/add/core.rb A lib/json/add/rails.rb A test/json/test_json_rails.rb git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@14051 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
e46a617759
commit
a049c43c65
4 changed files with 305 additions and 12 deletions
26
lib/json.rb
26
lib/json.rb
|
@ -62,6 +62,13 @@ require 'json/common'
|
|||
# you
|
||||
# require 'json/add/core'
|
||||
#
|
||||
# After requiring this you can, e. g., serialise/deserialise Ruby ranges:
|
||||
#
|
||||
# JSON JSON(1..10) # => 1..10
|
||||
#
|
||||
# To find out how to add JSON support to other or your own classes, read the
|
||||
# Examples section below.
|
||||
#
|
||||
# To get the best compatibility to rails' JSON implementation, you can
|
||||
# require 'json/add/rails'
|
||||
#
|
||||
|
@ -125,11 +132,6 @@ require 'json/common'
|
|||
# json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
||||
# # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
|
||||
#
|
||||
# It's also possible to call the #to_json method directly.
|
||||
#
|
||||
# json = [1, 2, {"a"=>3.141}, false, true, nil, 4..10].to_json
|
||||
# # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
|
||||
#
|
||||
# To create a valid JSON text you have to make sure, that the output is
|
||||
# embedded in either a JSON array [] or a JSON object {}. The easiest way to do
|
||||
# this, is by putting your values in a Ruby Array or Hash instance.
|
||||
|
@ -145,10 +147,10 @@ require 'json/common'
|
|||
# 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 extend JSON to support serialization of arbitrary classes by
|
||||
# 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):
|
||||
# should return a JSON object (a hash converted to JSON with #to_json) like
|
||||
# this (don't forget the *a for all the arguments):
|
||||
#
|
||||
# class Range
|
||||
# def to_json(*a)
|
||||
|
@ -159,15 +161,15 @@ require 'json/common'
|
|||
# end
|
||||
# end
|
||||
#
|
||||
# The hash key 'json_class' is the class, that will be asked to deserialize the
|
||||
# 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 deserialized.
|
||||
# used to store the necessary data to configure the object to be deserialised.
|
||||
#
|
||||
# If a 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 deserialized by implementing Range.json_create like this:
|
||||
# be deserialised by implementing Range.json_create like this:
|
||||
#
|
||||
# class Range
|
||||
# def self.json_create(o)
|
||||
|
@ -175,7 +177,7 @@ require 'json/common'
|
|||
# end
|
||||
# end
|
||||
#
|
||||
# Now it possible to serialize/deserialize ranges as well:
|
||||
# Now it possible to serialise/deserialise ranges as well:
|
||||
#
|
||||
# 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]}]"
|
||||
|
|
120
lib/json/add/core.rb
Normal file
120
lib/json/add/core.rb
Normal file
|
@ -0,0 +1,120 @@
|
|||
# This file contains implementations of ruby core's custom objects for
|
||||
# serialisation/deserialisation.
|
||||
|
||||
unless Object.const_defined?(:JSON) and ::JSON.const_defined?(:JSON_LOADED) and
|
||||
::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
require 'date'
|
||||
|
||||
class Time
|
||||
def self.json_create(object)
|
||||
at(*object.values_at('s', 'u'))
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name.to_s,
|
||||
's' => tv_sec,
|
||||
'u' => tv_usec,
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class Date
|
||||
def self.json_create(object)
|
||||
civil(*object.values_at('y', 'm', 'd', 'sg'))
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name.to_s,
|
||||
'y' => year,
|
||||
'm' => month,
|
||||
'd' => day,
|
||||
'sg' => @sg,
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class DateTime
|
||||
def self.json_create(object)
|
||||
args = object.values_at('y', 'm', 'd', 'H', 'M', 'S')
|
||||
of_a, of_b = object['of'].split('/')
|
||||
args << Rational(of_a.to_i, of_b.to_i)
|
||||
args << object['sg']
|
||||
civil(*args)
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name.to_s,
|
||||
'y' => year,
|
||||
'm' => month,
|
||||
'd' => day,
|
||||
'H' => hour,
|
||||
'M' => min,
|
||||
'S' => sec,
|
||||
'of' => offset.to_s,
|
||||
'sg' => @sg,
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class Range
|
||||
def self.json_create(object)
|
||||
new(*object['a'])
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name.to_s,
|
||||
'a' => [ first, last, exclude_end? ]
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class Struct
|
||||
def self.json_create(object)
|
||||
new(*object['v'])
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
klass = self.class.name.to_s
|
||||
klass.empty? and raise JSON::JSONError, "Only named structs are supported!"
|
||||
{
|
||||
'json_class' => klass,
|
||||
'v' => values,
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class Exception
|
||||
def self.json_create(object)
|
||||
result = new(object['m'])
|
||||
result.set_backtrace object['b']
|
||||
result
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name.to_s,
|
||||
'm' => message,
|
||||
'b' => backtrace,
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class Regexp
|
||||
def self.json_create(object)
|
||||
new(object['s'], object['o'])
|
||||
end
|
||||
|
||||
def to_json(*)
|
||||
{
|
||||
'json_class' => self.class.name.to_s,
|
||||
'o' => options,
|
||||
's' => source,
|
||||
}.to_json
|
||||
end
|
||||
end
|
58
lib/json/add/rails.rb
Normal file
58
lib/json/add/rails.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
# This file contains implementations of rails custom objects for
|
||||
# serialisation/deserialisation.
|
||||
|
||||
unless Object.const_defined?(:JSON) and ::JSON.const_defined?(:JSON_LOADED) and
|
||||
::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
|
||||
class Object
|
||||
def self.json_create(object)
|
||||
obj = new
|
||||
for key, value in object
|
||||
next if key == 'json_class'
|
||||
instance_variable_set "@#{key}", value
|
||||
end
|
||||
obj
|
||||
end
|
||||
|
||||
def to_json(*a)
|
||||
result = {
|
||||
'json_class' => self.class.name
|
||||
}
|
||||
instance_variables.inject(result) do |r, name|
|
||||
r[name[1..-1]] = instance_variable_get name
|
||||
r
|
||||
end
|
||||
result.to_json(*a)
|
||||
end
|
||||
end
|
||||
|
||||
class Symbol
|
||||
def to_json(*a)
|
||||
to_s.to_json(*a)
|
||||
end
|
||||
end
|
||||
|
||||
module Enumerable
|
||||
def to_json(*a)
|
||||
to_a.to_json(*a)
|
||||
end
|
||||
end
|
||||
|
||||
# class Regexp
|
||||
# def to_json(*)
|
||||
# inspect
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# The above rails definition has some problems:
|
||||
#
|
||||
# 1. { 'foo' => /bar/ }.to_json # => "{foo: /bar/}"
|
||||
# This isn't valid JSON, because the regular expression syntax is not
|
||||
# defined in RFC 4627. (And unquoted strings are disallowed there, too.)
|
||||
# Though it is valid Javascript.
|
||||
#
|
||||
# 2. { 'foo' => /bar/mix }.to_json # => "{foo: /bar/mix}"
|
||||
# This isn't even valid Javascript.
|
||||
|
113
test/json/test_json_rails.rb
Normal file
113
test/json/test_json_rails.rb
Normal file
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'test/unit'
|
||||
require 'json/add/rails'
|
||||
require 'date'
|
||||
|
||||
class TC_JSONRails < Test::Unit::TestCase
|
||||
include JSON
|
||||
|
||||
class A
|
||||
def initialize(a)
|
||||
@a = a
|
||||
end
|
||||
|
||||
attr_reader :a
|
||||
|
||||
def ==(other)
|
||||
a == other.a
|
||||
end
|
||||
|
||||
def self.json_create(object)
|
||||
new(*object['args'])
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
'args' => [ @a ],
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class B
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class C
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => 'TC_JSONRails::Nix',
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
end
|
||||
|
||||
def test_extended_json
|
||||
a = A.new(666)
|
||||
assert A.json_creatable?
|
||||
json = generate(a)
|
||||
a_again = JSON.parse(json)
|
||||
assert_kind_of a.class, a_again
|
||||
assert_equal a, a_again
|
||||
end
|
||||
|
||||
def test_extended_json_disabled
|
||||
a = A.new(666)
|
||||
assert A.json_creatable?
|
||||
json = generate(a)
|
||||
a_again = JSON.parse(json, :create_additions => true)
|
||||
assert_kind_of a.class, a_again
|
||||
assert_equal a, a_again
|
||||
a_hash = JSON.parse(json, :create_additions => false)
|
||||
assert_kind_of Hash, a_hash
|
||||
assert_equal(
|
||||
{"args"=>[666], "json_class"=>"TC_JSONRails::A"}.sort_by { |k,| k },
|
||||
a_hash.sort_by { |k,| k }
|
||||
)
|
||||
end
|
||||
|
||||
def test_extended_json_fail
|
||||
b = B.new
|
||||
assert !B.json_creatable?
|
||||
json = generate(b)
|
||||
assert_equal({ 'json_class' => B.name }, JSON.parse(json))
|
||||
end
|
||||
|
||||
def test_extended_json_fail
|
||||
c = C.new # with rails addition all objects are theoretically creatable
|
||||
assert C.json_creatable?
|
||||
json = generate(c)
|
||||
assert_raises(ArgumentError) { JSON.parse(json) }
|
||||
end
|
||||
|
||||
def test_raw_strings
|
||||
raw = ''
|
||||
raw_array = []
|
||||
for i in 0..255
|
||||
raw << i
|
||||
raw_array << i
|
||||
end
|
||||
json = raw.to_json_raw
|
||||
json_raw_object = raw.to_json_raw_object
|
||||
hash = { 'json_class' => 'String', 'raw'=> raw_array }
|
||||
assert_equal hash, json_raw_object
|
||||
json_raw = <<EOT.chomp
|
||||
{\"json_class\":\"String\",\"raw\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]}
|
||||
EOT
|
||||
# "
|
||||
assert_equal json_raw, json
|
||||
raw_again = JSON.parse(json)
|
||||
assert_equal raw, raw_again
|
||||
end
|
||||
|
||||
def test_symbol
|
||||
assert_equal '"foo"', JSON(:foo) # we don't want an object here
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue