1
0
Fork 0
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:
naruse 2007-11-30 12:08:46 +00:00
parent e46a617759
commit a049c43c65
4 changed files with 305 additions and 12 deletions

View file

@ -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
View 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
View 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.

View 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