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, ext/json, test/json:

import JSON library.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12428 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
naruse 2007-06-04 12:31:26 +00:00
parent 6b3ef2249c
commit af1c416728
69 changed files with 8551 additions and 70 deletions

View file

@ -0,0 +1 @@
"A JSON payload should be an object or array, not a string."

View file

@ -0,0 +1 @@
{"Extra value after close": true} "misplaced quoted value"

View file

@ -0,0 +1 @@
{"Illegal expression": 1 + 2}

View file

@ -0,0 +1 @@
{"Illegal invocation": alert()}

View file

@ -0,0 +1 @@
{"Numbers cannot have leading zeroes": 013}

View file

@ -0,0 +1 @@
{"Numbers cannot be hex": 0x14}

View file

@ -0,0 +1 @@
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]] // No, we don't limit our depth: Moved to pass...

View file

@ -0,0 +1 @@
{"Missing colon" null}

View file

@ -0,0 +1 @@
["Unclosed array"

View file

@ -0,0 +1 @@
{"Double colon":: null}

View file

@ -0,0 +1 @@
{"Comma instead of colon", null}

View file

@ -0,0 +1 @@
["Colon instead of comma": false]

View file

@ -0,0 +1 @@
["Bad value", truth]

View file

@ -0,0 +1 @@
['single quote']

View file

@ -0,0 +1 @@
["tab character in string "]

View file

@ -0,0 +1,2 @@
["line
break"]

View file

@ -0,0 +1,2 @@
["line\
break"]

View file

@ -0,0 +1 @@
{unquoted_key: "keys must be quoted"}

View file

@ -0,0 +1 @@
["extra comma",]

View file

@ -0,0 +1 @@
["double extra comma",,]

View file

@ -0,0 +1 @@
[ , "<-- missing value"]

View file

@ -0,0 +1 @@
["Comma after the close"],

View file

@ -0,0 +1 @@
["Extra close"]]

View file

@ -0,0 +1 @@
{"Extra comma": true,}

View file

@ -0,0 +1,56 @@
[
"JSON Test Pattern pass1",
{"object with 1 member":["array with 1 element"]},
{},
[],
-42,
true,
false,
null,
{
"integer": 1234567890,
"real": -9876.543210,
"e": 0.123456789e-12,
"E": 1.234567890E+34,
"": 23456789012E666,
"zero": 0,
"one": 1,
"space": " ",
"quote": "\"",
"backslash": "\\",
"controls": "\b\f\n\r\t",
"slash": "/ & \/",
"alpha": "abcdefghijklmnopqrstuvwyz",
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
"digit": "0123456789",
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
"true": true,
"false": false,
"null": null,
"array":[ ],
"object":{ },
"address": "50 St. James Street",
"url": "http://www.JSON.org/",
"comment": "// /* <!-- --",
"# -- --> */": " ",
" s p a c e d " :[1,2 , 3
,
4 , 5 , 6 ,7 ],
"compact": [1,2,3,4,5,6,7],
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
"quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
: "A key can be any string"
},
0.5 ,98.6
,
99.44
,
1066
,"rosebud"]

View file

@ -0,0 +1 @@
["Illegal backslash escape: \x15"]

View file

@ -0,0 +1 @@
["Illegal backslash escape: \'"]

View file

@ -0,0 +1 @@
["Illegal backslash escape: \017"]

View file

@ -0,0 +1 @@
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]

View file

@ -0,0 +1 @@
["tab\ character\ in\ string\ "]

View file

@ -0,0 +1,6 @@
{
"JSON Test Pattern pass3": {
"The outermost value": "must be an object or array.",
"In this test": "It is an object."
}
}

24
test/json/runner.rb Normal file
View file

@ -0,0 +1,24 @@
#!/usr/bin/env ruby
require 'test/unit/ui/console/testrunner'
require 'test/unit/testsuite'
$:.unshift File.expand_path(File.dirname($0))
$:.unshift 'tests'
require 'test_json'
require 'test_json_generate'
require 'test_json_unicode'
require 'test_json_addition'
require 'test_json_fixtures'
class TS_AllTests
def self.suite
suite = Test::Unit::TestSuite.new name
suite << TC_JSONGenerate.suite
suite << TC_JSON.suite
suite << TC_JSONUnicode.suite
suite << TC_JSONAddition.suite
suite << TC_JSONFixtures.suite
end
end
Test::Unit::UI::Console::TestRunner.run(TS_AllTests)
# vim: set et sw=2 ts=2:

255
test/json/test_json.rb Executable file
View file

@ -0,0 +1,255 @@
#!/usr/bin/env ruby
require 'test/unit'
require 'json'
class TC_JSON < Test::Unit::TestCase
include JSON
def setup
$KCODE = 'UTF8'
@ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true].map do
|x| [x]
end
@ary_to_parse = ["1", '"foo"', "3.14", "4711.0", "2.718", "null",
"[1,-2,3]", "false", "true"].map do
|x| "[#{x}]"
end
@hash = {
'a' => 2,
'b' => 3.141,
'c' => 'c',
'd' => [ 1, "b", 3.14 ],
'e' => { 'foo' => 'bar' },
'g' => "\"\0\037",
'h' => 1000.0,
'i' => 0.001
}
@json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
'"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}'
end
suite << TC_JSON.suite
def test_construction
parser = JSON::Parser.new('test')
assert_equal 'test', parser.source
end
def assert_equal_float(expected, is)
assert_in_delta(expected.first, is.first, 1e-2)
end
def test_parse_simple_arrays
assert_equal([], parse('[]'))
assert_equal([], parse(' [ ] '))
assert_equal([nil], parse('[null]'))
assert_equal([false], parse('[false]'))
assert_equal([true], parse('[true]'))
assert_equal([-23], parse('[-23]'))
assert_equal([23], parse('[23]'))
assert_equal([0.23], parse('[0.23]'))
assert_equal([0.0], parse('[0e0]'))
assert_raises(JSON::ParserError) { parse('[+23.2]') }
assert_raises(JSON::ParserError) { parse('[+23]') }
assert_raises(JSON::ParserError) { parse('[.23]') }
assert_raises(JSON::ParserError) { parse('[023]') }
assert_equal_float [3.141], parse('[3.141]')
assert_equal_float [-3.141], parse('[-3.141]')
assert_equal_float [3.141], parse('[3141e-3]')
assert_equal_float [3.141], parse('[3141.1e-3]')
assert_equal_float [3.141], parse('[3141E-3]')
assert_equal_float [3.141], parse('[3141.0E-3]')
assert_equal_float [-3.141], parse('[-3141.0e-3]')
assert_equal_float [-3.141], parse('[-3141e-3]')
assert_equal([""], parse('[""]'))
assert_equal(["foobar"], parse('["foobar"]'))
assert_equal([{}], parse('[{}]'))
end
def test_parse_simple_objects
assert_equal({}, parse('{}'))
assert_equal({}, parse(' { } '))
assert_equal({ "a" => nil }, parse('{ "a" : null}'))
assert_equal({ "a" => nil }, parse('{"a":null}'))
assert_equal({ "a" => false }, parse('{ "a" : false } '))
assert_equal({ "a" => false }, parse('{"a":false}'))
assert_raises(JSON::ParserError) { parse('{false}') }
assert_equal({ "a" => true }, parse('{"a":true}'))
assert_equal({ "a" => true }, parse(' { "a" : true } '))
assert_equal({ "a" => -23 }, parse(' { "a" : -23 } '))
assert_equal({ "a" => -23 }, parse(' { "a" : -23 } '))
assert_equal({ "a" => 23 }, parse('{"a":23 } '))
assert_equal({ "a" => 23 }, parse(' { "a" : 23 } '))
assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } '))
assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } '))
end
begin
require 'permutation'
def test_parse_more_complex_arrays
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
perms = Permutation.for a
perms.each do |perm|
orig_ary = perm.project
json = pretty_generate(orig_ary)
assert_equal orig_ary, parse(json)
end
end
def test_parse_complex_objects
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
perms = Permutation.for a
perms.each do |perm|
s = "a"
orig_obj = perm.project.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
json = pretty_generate(orig_obj)
assert_equal orig_obj, parse(json)
end
end
rescue LoadError
warn "Skipping permutation tests."
end
def test_parse_arrays
assert_equal([1,2,3], parse('[1,2,3]'))
assert_equal([1.2,2,3], parse('[1.2,2,3]'))
assert_equal([[],[[],[]]], parse('[[],[[],[]]]'))
end
def test_parse_values
assert_equal([""], parse('[""]'))
assert_equal(["\\"], parse('["\\\\"]'))
assert_equal(['"'], parse('["\""]'))
assert_equal(['\\"\\'], parse('["\\\\\\"\\\\"]'))
assert_equal(["\"\b\n\r\t\0\037"],
parse('["\"\b\n\r\t\u0000\u001f"]'))
for i in 0 ... @ary.size
assert_equal(@ary[i], parse(@ary_to_parse[i]))
end
end
def test_parse_array
assert_equal([], parse('[]'))
assert_equal([], parse(' [ ] '))
assert_equal([1], parse('[1]'))
assert_equal([1], parse(' [ 1 ] '))
assert_equal(@ary,
parse('[[1],["foo"],[3.14],[47.11e+2],[2718.0E-3],[null],[[1,-2,3]]'\
',[false],[true]]'))
assert_equal(@ary, parse(%Q{ [ [1] , ["foo"] , [3.14] \t , [47.11e+2]
, [2718.0E-3 ],\r[ null] , [[1, -2, 3 ]], [false ],[ true]\n ] }))
end
def test_parse_object
assert_equal({}, parse('{}'))
assert_equal({}, parse(' { } '))
assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}'))
assert_equal({'foo'=>'bar'}, parse(' { "foo" : "bar" } '))
end
def test_parser_reset
parser = Parser.new(@json)
assert_equal(@hash, parser.parse)
assert_equal(@hash, parser.parse)
end
def test_comments
json = <<EOT
{
"key1":"value1", // eol comment
"key2":"value2" /* multi line
* comment */,
"key3":"value3" /* multi line
// nested eol comment
* comment */
}
EOT
assert_equal(
{ "key1" => "value1", "key2" => "value2", "key3" => "value3" },
parse(json))
json = <<EOT
{
"key1":"value1" /* multi line
// nested eol comment
/* illegal nested multi line comment */
* comment */
}
EOT
assert_raises(ParserError) { parse(json) }
json = <<EOT
{
"key1":"value1" /* multi line
// nested eol comment
closed multi comment */
and again, throw an Error */
}
EOT
assert_raises(ParserError) { parse(json) }
json = <<EOT
{
"key1":"value1" /*/*/
}
EOT
assert_equal({ "key1" => "value1" }, parse(json))
end
def test_backslash
data = [ '\\.(?i:gif|jpe?g|png)$' ]
json = '["\\\\.(?i:gif|jpe?g|png)$"]'
assert_equal json, JSON.unparse(data)
assert_equal data, JSON.parse(json)
#
data = [ '\\"' ]
json = '["\\\\\""]'
assert_equal json, JSON.unparse(data)
assert_equal data, JSON.parse(json)
#
json = '["\/"]'
data = JSON.parse(json)
assert_equal ['/'], data
assert_equal json, JSON.unparse(data)
#
json = '["\""]'
data = JSON.parse(json)
assert_equal ['"'], data
assert_equal json, JSON.unparse(data)
json = '["\\\'"]'
data = JSON.parse(json)
assert_equal ["'"], data
assert_equal '["\'"]', JSON.unparse(data)
end
def test_wrong_inputs
assert_raises(ParserError) { JSON.parse('"foo"') }
assert_raises(ParserError) { JSON.parse('123') }
assert_raises(ParserError) { JSON.parse('[] bla') }
assert_raises(ParserError) { JSON.parse('[] 1') }
assert_raises(ParserError) { JSON.parse('[] []') }
assert_raises(ParserError) { JSON.parse('[] {}') }
assert_raises(ParserError) { JSON.parse('{} []') }
assert_raises(ParserError) { JSON.parse('{} {}') }
assert_raises(ParserError) { JSON.parse('[NULL]') }
assert_raises(ParserError) { JSON.parse('[FALSE]') }
assert_raises(ParserError) { JSON.parse('[TRUE]') }
assert_raises(ParserError) { JSON.parse('[07] ') }
assert_raises(ParserError) { JSON.parse('[0a]') }
assert_raises(ParserError) { JSON.parse('[1.]') }
assert_raises(ParserError) { JSON.parse(' ') }
end
def test_nesting
to_deep = '[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]'
assert_raises(JSON::NestingError) { JSON.parse to_deep }
assert_raises(JSON::NestingError) { JSON.parser.new(to_deep).parse }
assert_raises(JSON::NestingError) { JSON.parse to_deep, :max_nesting => 19 }
ok = JSON.parse to_deep, :max_nesting => 20
assert_kind_of Array, ok
ok = JSON.parse to_deep, :max_nesting => nil
assert_kind_of Array, ok
ok = JSON.parse to_deep, :max_nesting => false
assert_kind_of Array, ok
ok = JSON.parse to_deep, :max_nesting => 0
assert_kind_of Array, ok
end
end
# vim: set et sw=2 ts=2:

94
test/json/test_json_addition.rb Executable file
View file

@ -0,0 +1,94 @@
#!/usr/bin/env ruby
require 'test/unit'
require 'json'
class TC_JSONAddition < 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,
'args' => [ @a ],
}.to_json(*args)
end
end
class B
def to_json(*args)
{
'json_class' => self.class,
}.to_json(*args)
end
end
class C
def to_json(*args)
{
'json_class' => 'TC_JSONAddition::Nix',
}.to_json(*args)
end
end
def setup
$KCODE = 'UTF8'
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_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
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
end

30
test/json/test_json_fixtures.rb Executable file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env ruby
require 'test/unit'
require 'json'
class TC_JSONFixtures < Test::Unit::TestCase
def setup
$KCODE = 'UTF8'
fixtures = File.join(File.dirname(__FILE__), 'fixtures/*.json')
passed, failed = Dir[fixtures].partition { |f| f['pass'] }
@passed = passed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
@failed = failed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
end
def test_passing
for name, source in @passed
assert JSON.parse(source),
"Did not pass for fixture '#{name}'"
end
end
def test_failing
for name, source in @failed
assert_raises(JSON::ParserError, JSON::NestingError,
"Did not fail for fixture '#{name}'") do
JSON.parse(source)
end
end
end
end

81
test/json/test_json_generate.rb Executable file
View file

@ -0,0 +1,81 @@
require 'test/unit'
require 'json'
class TC_JSONGenerate < Test::Unit::TestCase
include JSON
def setup
$KCODE = 'UTF8'
@hash = {
'a' => 2,
'b' => 3.141,
'c' => 'c',
'd' => [ 1, "b", 3.14 ],
'e' => { 'foo' => 'bar' },
'g' => "\"\0\037",
'h' => 1000.0,
'i' => 0.001
}
@json2 = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
@json3 = <<'EOT'.chomp
{
"a": 2,
"b": 3.141,
"c": "c",
"d": [
1,
"b",
3.14
],
"e": {
"foo": "bar"
},
"g": "\"\u0000\u001f",
"h": 1000.0,
"i": 0.001
}
EOT
end
def test_unparse
json = unparse(@hash)
assert_equal(@json2, json)
parsed_json = parse(json)
assert_equal(@hash, parsed_json)
json = generate({1=>2})
assert_equal('{"1":2}', json)
parsed_json = parse(json)
assert_equal({"1"=>2}, parsed_json)
end
def test_unparse_pretty
json = pretty_unparse(@hash)
assert_equal(@json3, json)
parsed_json = parse(json)
assert_equal(@hash, parsed_json)
json = pretty_generate({1=>2})
assert_equal(<<'EOT'.chomp, json)
{
"1": 2
}
EOT
parsed_json = parse(json)
assert_equal({"1"=>2}, parsed_json)
end
def test_states
json = generate({1=>2}, nil)
assert_equal('{"1":2}', json)
s = JSON.state.new(:check_circular => true)
#assert s.check_circular
h = { 1=>2 }
h[3] = h
assert_raises(JSON::CircularDatastructure) { generate(h, s) }
s = JSON.state.new(:check_circular => true)
#assert s.check_circular
a = [ 1, 2 ]
a << a
assert_raises(JSON::CircularDatastructure) { generate(a, s) }
end
end

59
test/json/test_json_unicode.rb Executable file
View file

@ -0,0 +1,59 @@
#!/usr/bin/env ruby
require 'test/unit'
require 'json'
class TC_JSONUnicode < Test::Unit::TestCase
include JSON
def setup
$KCODE = 'UTF8'
end
def test_unicode
assert_equal '""', ''.to_json
assert_equal '"\\b"', "\b".to_json
assert_equal '"\u0001"', 0x1.chr.to_json
assert_equal '"\u001f"', 0x1f.chr.to_json
assert_equal '" "', ' '.to_json
assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
utf8 = [ "© ≠ €! \01" ]
json = '["\u00a9 \u2260 \u20ac! \u0001"]'
assert_equal json, utf8.to_json
assert_equal utf8, parse(json)
utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]"
assert_equal json, utf8.to_json
assert_equal utf8, parse(json)
utf8 = ['საქართველო']
json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]"
assert_equal json, utf8.to_json
assert_equal utf8, parse(json)
assert_equal '["\\u00c3"]', JSON.generate(["Ã"])
assert_equal [""], JSON.parse('["\u20ac"]')
utf8 = ["\xf0\xa0\x80\x81"]
json = '["\ud840\udc01"]'
assert_equal json, JSON.generate(utf8)
assert_equal utf8, JSON.parse(json)
end
def test_chars
(0..0x7f).each do |i|
c = ('%c' % i)[0] # c is a character object
json = '["\u%04x"]' % i
assert_equal c, JSON.parse(json).first
if c == ?\b
generated = JSON.generate(["" << i])
assert '["\b"]' == generated || '["\10"]' == generated
elsif [?\n, ?\r, ?\t, ?\f].include?(c)
assert_equal '[' << ('' << i).dump << ']', JSON.generate(["" << i])
elsif i < 0x20
assert_equal json, JSON.generate(["" << i])
end
end
assert_raises(JSON::GeneratorError) do
JSON.generate(["" << 0x80])
end
assert_equal "\302\200", JSON.parse('["\u0080"]').first
end
end