json 0.4.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of json might be problematic. Click here for more details.
- data/CHANGES +6 -1
- data/README +49 -7
- data/Rakefile +216 -52
- data/TODO +1 -0
- data/VERSION +1 -1
- data/benchmarks/benchmark.txt +133 -0
- data/benchmarks/benchmark_generator.rb +44 -0
- data/benchmarks/benchmark_parser.rb +22 -0
- data/benchmarks/benchmark_rails.rb +26 -0
- data/data/example.json +1 -0
- data/data/index.html +37 -0
- data/data/prototype.js +2515 -0
- data/ext/json/ext/generator/Makefile +149 -0
- data/ext/json/ext/generator/extconf.rb +9 -0
- data/ext/json/ext/generator/generator.c +729 -0
- data/ext/json/ext/generator/unicode.c +184 -0
- data/ext/json/ext/generator/unicode.h +40 -0
- data/ext/json/ext/parser/Makefile +149 -0
- data/ext/json/ext/parser/extconf.rb +9 -0
- data/ext/json/ext/parser/parser.c +1551 -0
- data/ext/json/ext/parser/parser.rl +515 -0
- data/ext/json/ext/parser/unicode.c +156 -0
- data/ext/json/ext/parser/unicode.h +44 -0
- data/install.rb +13 -8
- data/lib/json.rb +101 -614
- data/lib/json/common.rb +184 -0
- data/lib/json/editor.rb +19 -10
- data/lib/json/ext.rb +13 -0
- data/lib/json/pure.rb +75 -0
- data/lib/json/pure/generator.rb +321 -0
- data/lib/json/pure/parser.rb +210 -0
- data/lib/json/version.rb +8 -0
- data/tests/fixtures/fail1.json +1 -0
- data/tests/fixtures/fail10.json +1 -0
- data/tests/fixtures/fail11.json +1 -0
- data/tests/fixtures/fail12.json +1 -0
- data/tests/fixtures/fail13.json +1 -0
- data/tests/fixtures/fail14.json +1 -0
- data/tests/fixtures/fail15.json +1 -0
- data/tests/fixtures/fail16.json +1 -0
- data/tests/fixtures/fail17.json +1 -0
- data/tests/fixtures/fail19.json +1 -0
- data/tests/fixtures/fail2.json +1 -0
- data/tests/fixtures/fail20.json +1 -0
- data/tests/fixtures/fail21.json +1 -0
- data/tests/fixtures/fail22.json +1 -0
- data/tests/fixtures/fail23.json +1 -0
- data/tests/fixtures/fail24.json +1 -0
- data/tests/fixtures/fail25.json +1 -0
- data/tests/fixtures/fail26.json +1 -0
- data/tests/fixtures/fail27.json +2 -0
- data/tests/fixtures/fail28.json +2 -0
- data/tests/fixtures/fail3.json +1 -0
- data/tests/fixtures/fail4.json +1 -0
- data/tests/fixtures/fail5.json +1 -0
- data/tests/fixtures/fail6.json +1 -0
- data/tests/fixtures/fail7.json +1 -0
- data/tests/fixtures/fail8.json +1 -0
- data/tests/fixtures/fail9.json +1 -0
- data/tests/fixtures/pass1.json +56 -0
- data/tests/fixtures/pass18.json +1 -0
- data/tests/fixtures/pass2.json +1 -0
- data/tests/fixtures/pass3.json +6 -0
- data/tests/runner.rb +8 -2
- data/tests/test_json.rb +102 -154
- data/tests/test_json_addition.rb +94 -0
- data/tests/test_json_fixtures.rb +30 -0
- data/tests/test_json_generate.rb +81 -0
- data/tests/test_json_unicode.rb +55 -0
- data/tools/fuzz.rb +133 -0
- data/tools/server.rb +62 -0
- metadata +87 -10
- data/bla.json.tmp +0 -0
- data/lib/json.rb.orig +0 -708
metadata
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.
|
2
|
+
rubygems_version: 0.9.2
|
3
3
|
specification_version: 1
|
4
4
|
name: json
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
8
|
-
summary: A JSON implementation
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2007-03-27 00:00:00 +02:00
|
8
|
+
summary: A JSON implementation as a Ruby extension
|
9
9
|
require_paths:
|
10
|
+
- ext/json/ext
|
11
|
+
- ext
|
10
12
|
- lib
|
11
13
|
email: flori@ping.de
|
12
14
|
homepage: http://json.rubyforge.org
|
@@ -35,37 +37,112 @@ files:
|
|
35
37
|
- tests
|
36
38
|
- GPL
|
37
39
|
- install.rb
|
40
|
+
- ext
|
41
|
+
- diagrams
|
42
|
+
- benchmarks
|
38
43
|
- Rakefile
|
39
|
-
-
|
44
|
+
- data
|
40
45
|
- lib
|
41
46
|
- README
|
47
|
+
- tools
|
42
48
|
- CHANGES
|
43
49
|
- bin/edit_json.rb
|
50
|
+
- tests/test_json_fixtures.rb
|
44
51
|
- tests/runner.rb
|
45
52
|
- tests/test_json.rb
|
46
|
-
-
|
53
|
+
- tests/fixtures
|
54
|
+
- tests/test_json_unicode.rb
|
55
|
+
- tests/test_json_generate.rb
|
56
|
+
- tests/test_json_addition.rb
|
57
|
+
- tests/fixtures/fail27.json
|
58
|
+
- tests/fixtures/fail22.json
|
59
|
+
- tests/fixtures/fail26.json
|
60
|
+
- tests/fixtures/fail16.json
|
61
|
+
- tests/fixtures/fail28.json
|
62
|
+
- tests/fixtures/fail25.json
|
63
|
+
- tests/fixtures/pass18.json
|
64
|
+
- tests/fixtures/fail9.json
|
65
|
+
- tests/fixtures/fail20.json
|
66
|
+
- tests/fixtures/fail24.json
|
67
|
+
- tests/fixtures/fail14.json
|
68
|
+
- tests/fixtures/fail4.json
|
69
|
+
- tests/fixtures/fail7.json
|
70
|
+
- tests/fixtures/fail10.json
|
71
|
+
- tests/fixtures/fail13.json
|
72
|
+
- tests/fixtures/fail6.json
|
73
|
+
- tests/fixtures/fail21.json
|
74
|
+
- tests/fixtures/fail23.json
|
75
|
+
- tests/fixtures/fail3.json
|
76
|
+
- tests/fixtures/fail1.json
|
77
|
+
- tests/fixtures/fail11.json
|
78
|
+
- tests/fixtures/fail5.json
|
79
|
+
- tests/fixtures/pass1.json
|
80
|
+
- tests/fixtures/fail12.json
|
81
|
+
- tests/fixtures/fail15.json
|
82
|
+
- tests/fixtures/pass3.json
|
83
|
+
- tests/fixtures/fail8.json
|
84
|
+
- tests/fixtures/fail17.json
|
85
|
+
- tests/fixtures/fail19.json
|
86
|
+
- tests/fixtures/pass2.json
|
87
|
+
- tests/fixtures/fail2.json
|
88
|
+
- ext/json
|
89
|
+
- ext/json/ext
|
90
|
+
- ext/json/ext/generator
|
91
|
+
- ext/json/ext/parser
|
92
|
+
- ext/json/ext/generator/Makefile
|
93
|
+
- ext/json/ext/generator/unicode.c
|
94
|
+
- ext/json/ext/generator/unicode.h
|
95
|
+
- ext/json/ext/generator/extconf.rb
|
96
|
+
- ext/json/ext/generator/generator.c
|
97
|
+
- ext/json/ext/parser/Makefile
|
98
|
+
- ext/json/ext/parser/parser.c
|
99
|
+
- ext/json/ext/parser/unicode.c
|
100
|
+
- ext/json/ext/parser/parser.rl
|
101
|
+
- ext/json/ext/parser/unicode.h
|
102
|
+
- ext/json/ext/parser/extconf.rb
|
103
|
+
- benchmarks/benchmark_generator.rb
|
104
|
+
- benchmarks/benchmark.txt
|
105
|
+
- benchmarks/benchmark_parser.rb
|
106
|
+
- benchmarks/benchmark_rails.rb
|
107
|
+
- data/example.json
|
108
|
+
- data/prototype.js
|
109
|
+
- data/index.html
|
47
110
|
- lib/json
|
48
111
|
- lib/json.rb
|
49
112
|
- lib/json/FalseClass.xpm
|
50
113
|
- lib/json/TrueClass.xpm
|
114
|
+
- lib/json/ext.rb
|
115
|
+
- lib/json/common.rb
|
51
116
|
- lib/json/Hash.xpm
|
117
|
+
- lib/json/pure
|
52
118
|
- lib/json/Key.xpm
|
53
119
|
- lib/json/Numeric.xpm
|
54
120
|
- lib/json/Array.xpm
|
55
121
|
- lib/json/editor.rb
|
56
122
|
- lib/json/String.xpm
|
123
|
+
- lib/json/pure.rb
|
57
124
|
- lib/json/NilClass.xpm
|
125
|
+
- lib/json/version.rb
|
58
126
|
- lib/json/json.xpm
|
127
|
+
- lib/json/pure/parser.rb
|
128
|
+
- lib/json/pure/generator.rb
|
129
|
+
- tools/server.rb
|
130
|
+
- tools/fuzz.rb
|
59
131
|
test_files:
|
60
132
|
- tests/runner.rb
|
61
|
-
rdoc_options:
|
62
|
-
|
133
|
+
rdoc_options:
|
134
|
+
- --title
|
135
|
+
- JSON -- A JSON implemention
|
136
|
+
- --main
|
137
|
+
- JSON
|
138
|
+
- --line-numbers
|
63
139
|
extra_rdoc_files: []
|
64
140
|
|
65
141
|
executables:
|
66
142
|
- edit_json.rb
|
67
|
-
extensions:
|
68
|
-
|
143
|
+
extensions:
|
144
|
+
- ext/json/ext/parser/extconf.rb
|
145
|
+
- ext/json/ext/generator/extconf.rb
|
69
146
|
requirements: []
|
70
147
|
|
71
148
|
dependencies: []
|
data/bla.json.tmp
DELETED
File without changes
|
data/lib/json.rb.orig
DELETED
@@ -1,708 +0,0 @@
|
|
1
|
-
# = json - JSON library for Ruby
|
2
|
-
#
|
3
|
-
# == Description
|
4
|
-
#
|
5
|
-
# == Author
|
6
|
-
#
|
7
|
-
# Florian Frank <mailto:flori@ping.de>
|
8
|
-
#
|
9
|
-
# == License
|
10
|
-
#
|
11
|
-
# This is free software; you can redistribute it and/or modify it under the
|
12
|
-
# terms of the GNU General Public License Version 2 as published by the Free
|
13
|
-
# Software Foundation: www.gnu.org/copyleft/gpl.html
|
14
|
-
#
|
15
|
-
# == Download
|
16
|
-
#
|
17
|
-
# The latest version of this library can be downloaded at
|
18
|
-
#
|
19
|
-
# * http://rubyforge.org/frs?group_id=953
|
20
|
-
#
|
21
|
-
# Online Documentation should be located at
|
22
|
-
#
|
23
|
-
# * http://json.rubyforge.org
|
24
|
-
#
|
25
|
-
# == Examples
|
26
|
-
#
|
27
|
-
# To create a JSON string from a ruby data structure, you
|
28
|
-
# can call JSON.unparse like that:
|
29
|
-
#
|
30
|
-
# json = JSON.unparse [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
31
|
-
# # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
|
32
|
-
#
|
33
|
-
# It's also possible to call the #to_json method directly.
|
34
|
-
#
|
35
|
-
# json = [1, 2, {"a"=>3.141}, false, true, nil, 4..10].to_json
|
36
|
-
# # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
|
37
|
-
#
|
38
|
-
# To get back a ruby data structure, you have to call
|
39
|
-
# JSON.parse on the JSON string:
|
40
|
-
#
|
41
|
-
# JSON.parse json
|
42
|
-
# # => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
|
43
|
-
#
|
44
|
-
# Note, that the range from the original data structure is a simple
|
45
|
-
# string now. The reason for this is, that JSON doesn't support ranges
|
46
|
-
# or arbitrary classes. In this case the json library falls back to call
|
47
|
-
# Object#to_json, which is the same as #to_s.to_json.
|
48
|
-
#
|
49
|
-
# It's possible to extend JSON to support serialization of arbitray classes by
|
50
|
-
# simply implementing a more specialized version of the #to_json method, that
|
51
|
-
# should return a JSON object (a hash converted to JSON with #to_json)
|
52
|
-
# like this (don't forget the *a for all the arguments):
|
53
|
-
#
|
54
|
-
# class Range
|
55
|
-
# def to_json(*a)
|
56
|
-
# {
|
57
|
-
# 'json_class' => self.class.name,
|
58
|
-
# 'data' => [ first, last, exclude_end? ]
|
59
|
-
# }.to_json(*a)
|
60
|
-
# end
|
61
|
-
# end
|
62
|
-
#
|
63
|
-
# The hash key 'json_class' is the class, that will be asked to deserialize the
|
64
|
-
# JSON representation later. In this case it's 'Range', but any namespace of
|
65
|
-
# the form 'A::B' or '::A::B' will do. All other keys are arbitrary and can be
|
66
|
-
# used to store the necessary data to configure the object to be deserialized.
|
67
|
-
#
|
68
|
-
# If a the key 'json_class' is found in a JSON object, the JSON parser checks
|
69
|
-
# if the given class responds to the json_create class method. If so, it is
|
70
|
-
# called with the JSON object converted to a Ruby hash. So a range can
|
71
|
-
# be deserialized by implementing Range.json_create like this:
|
72
|
-
#
|
73
|
-
# class Range
|
74
|
-
# def self.json_create(o)
|
75
|
-
# new(*o['data'])
|
76
|
-
# end
|
77
|
-
# end
|
78
|
-
#
|
79
|
-
# Now it possible to serialize/deserialize ranges as well:
|
80
|
-
#
|
81
|
-
# json = JSON.unparse [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
82
|
-
# # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
|
83
|
-
# JSON.parse json
|
84
|
-
# # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
85
|
-
#
|
86
|
-
# JSON.unparse always creates the shortes possible string representation of a
|
87
|
-
# ruby data structure in one line. This good for data storage or network
|
88
|
-
# protocols, but not so good for humans to read. Fortunately there's
|
89
|
-
# also JSON.pretty_unparse that creates a more readable output:
|
90
|
-
#
|
91
|
-
# puts JSON.pretty_unparse([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
|
92
|
-
# [
|
93
|
-
# 1,
|
94
|
-
# 2,
|
95
|
-
# {
|
96
|
-
# "a": 3.141
|
97
|
-
# },
|
98
|
-
# false,
|
99
|
-
# true,
|
100
|
-
# null,
|
101
|
-
# {
|
102
|
-
# "json_class": "Range",
|
103
|
-
# "data": [
|
104
|
-
# 4,
|
105
|
-
# 10,
|
106
|
-
# false
|
107
|
-
# ]
|
108
|
-
# }
|
109
|
-
# ]
|
110
|
-
#
|
111
|
-
# There are also the methods Kernel#j for unparse, and Kernel#jj for
|
112
|
-
# pretty_unparse output to the console, that work analogous to Kernel#p and
|
113
|
-
# Kernel#pp.
|
114
|
-
#
|
115
|
-
|
116
|
-
require 'strscan'
|
117
|
-
|
118
|
-
# This module is the namespace for all the JSON related classes. It also
|
119
|
-
# defines some module functions to expose a nicer API to users, instead
|
120
|
-
# of using the parser and other classes directly.
|
121
|
-
module JSON
|
122
|
-
# The base exception for JSON errors.
|
123
|
-
JSONError = Class.new StandardError
|
124
|
-
|
125
|
-
# This exception is raise, if a parser error occurs.
|
126
|
-
ParserError = Class.new JSONError
|
127
|
-
|
128
|
-
# This exception is raise, if a unparser error occurs.
|
129
|
-
UnparserError = Class.new JSONError
|
130
|
-
|
131
|
-
# If a circular data structure is encountered while unparsing
|
132
|
-
# this exception is raised.
|
133
|
-
CircularDatastructure = Class.new UnparserError
|
134
|
-
|
135
|
-
class << self
|
136
|
-
# Switches on Unicode support, if _enable_ is _true_. Otherwise switches
|
137
|
-
# Unicode support off.
|
138
|
-
def support_unicode=(enable)
|
139
|
-
@support_unicode = enable
|
140
|
-
end
|
141
|
-
|
142
|
-
# Returns _true_ if JSON supports unicode, otherwise _false_ is returned.
|
143
|
-
#
|
144
|
-
# If loading of the iconv library fails, or it doesn't support utf8/utf16
|
145
|
-
# encoding, this will be set to false, as a fallback.
|
146
|
-
def support_unicode?
|
147
|
-
!!@support_unicode
|
148
|
-
end
|
149
|
-
end
|
150
|
-
JSON.support_unicode = true # default, however it's possible to switch off
|
151
|
-
# full unicode support, if non-ascii bytes should be
|
152
|
-
# just passed through.
|
153
|
-
|
154
|
-
begin
|
155
|
-
require 'iconv'
|
156
|
-
# An iconv instance to convert from UTF8 to UTF16 Big Endian.
|
157
|
-
UTF16toUTF8 = Iconv.new('utf-8', 'utf-16be')
|
158
|
-
# An iconv instance to convert from UTF16 Big Endian to UTF8.
|
159
|
-
UTF8toUTF16 = Iconv.new('utf-16be', 'utf-8'); UTF8toUTF16.iconv('no bom')
|
160
|
-
rescue Errno::EINVAL
|
161
|
-
begin
|
162
|
-
old_verbose = $VERBOSE
|
163
|
-
$VERBOSE = nil
|
164
|
-
# An iconv instance to convert from UTF8 to UTF16 Big Endian.
|
165
|
-
UTF16toUTF8 = Iconv.new('utf-8', 'utf-16')
|
166
|
-
# An iconv instance to convert from UTF16 Big Endian to UTF8.
|
167
|
-
UTF8toUTF16 = Iconv.new('utf-16', 'utf-8'); UTF8toUTF16.iconv('no bom')
|
168
|
-
if UTF8toUTF16.iconv("\xe2\x82\xac") == "\xac\x20"
|
169
|
-
swapper = Class.new do
|
170
|
-
def initialize(iconv)
|
171
|
-
@iconv = iconv
|
172
|
-
end
|
173
|
-
|
174
|
-
def iconv(string)
|
175
|
-
result = @iconv.iconv(string)
|
176
|
-
JSON.swap!(result)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
UTF8toUTF16 = swapper.new(UTF8toUTF16)
|
180
|
-
end
|
181
|
-
if UTF16toUTF8.iconv("\xac\x20") == "\xe2\x82\xac"
|
182
|
-
swapper = Class.new do
|
183
|
-
def initialize(iconv)
|
184
|
-
@iconv = iconv
|
185
|
-
end
|
186
|
-
|
187
|
-
def iconv(string)
|
188
|
-
string = JSON.swap!(string.dup)
|
189
|
-
@iconv.iconv(string)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
UTF16toUTF8 = swapper.new(UTF16toUTF8)
|
193
|
-
end
|
194
|
-
rescue Errno::EINVAL
|
195
|
-
# Enforce disabling of unicode support, if iconv doesn't support
|
196
|
-
# UTF8/UTF16 at all.
|
197
|
-
JSON.support_unicode = false
|
198
|
-
ensure
|
199
|
-
$VERBOSE = old_verbose
|
200
|
-
end
|
201
|
-
rescue LoadError
|
202
|
-
# Enforce disabling of unicode support, if iconv doesn't exist.
|
203
|
-
JSON.support_unicode = false
|
204
|
-
end
|
205
|
-
|
206
|
-
# Swap consecutive bytes in string in place.
|
207
|
-
def self.swap!(string)
|
208
|
-
0.upto(string.size / 2) do |i|
|
209
|
-
break unless string[2 * i + 1]
|
210
|
-
string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
|
211
|
-
end
|
212
|
-
string
|
213
|
-
end
|
214
|
-
|
215
|
-
# This class implements the JSON parser that is used to parse a JSON string
|
216
|
-
# into a Ruby data structure.
|
217
|
-
class Parser < StringScanner
|
218
|
-
STRING = /"((?:[^"\\]|\\.)*)"/
|
219
|
-
INTEGER = /-?\d+/
|
220
|
-
FLOAT = /-?\d+\.(\d*)(?i:e[+-]?\d+)?/
|
221
|
-
OBJECT_OPEN = /\{/
|
222
|
-
OBJECT_CLOSE = /\}/
|
223
|
-
ARRAY_OPEN = /\[/
|
224
|
-
ARRAY_CLOSE = /\]/
|
225
|
-
PAIR_DELIMITER = /:/
|
226
|
-
COLLECTION_DELIMITER = /,/
|
227
|
-
TRUE = /true/
|
228
|
-
FALSE = /false/
|
229
|
-
NULL = /null/
|
230
|
-
IGNORE = %r(
|
231
|
-
(?:
|
232
|
-
//[^\n\r]*[\n\r]| # line comments
|
233
|
-
/\* # c-style comments
|
234
|
-
(?:
|
235
|
-
[^*/]| # normal chars
|
236
|
-
/[^*]| # slashes that do not start a nested comment
|
237
|
-
\*[^/]| # asterisks that do not end this comment
|
238
|
-
/(?=\*/) # single slash before this comment's end
|
239
|
-
)*
|
240
|
-
\*/ # the end of this comment
|
241
|
-
|\s+ # whitespaces
|
242
|
-
)+
|
243
|
-
)mx
|
244
|
-
|
245
|
-
UNPARSED = Object.new
|
246
|
-
|
247
|
-
# Parses the current JSON string and returns the complete data structure
|
248
|
-
# as a result.
|
249
|
-
def parse
|
250
|
-
reset
|
251
|
-
until eos?
|
252
|
-
case
|
253
|
-
when scan(ARRAY_OPEN)
|
254
|
-
return parse_array
|
255
|
-
when scan(OBJECT_OPEN)
|
256
|
-
return parse_object
|
257
|
-
when skip(IGNORE)
|
258
|
-
;
|
259
|
-
when !((value = parse_value).equal? UNPARSED)
|
260
|
-
return value
|
261
|
-
else
|
262
|
-
raise ParserError, "source '#{peek(20)}' not in JSON!"
|
263
|
-
end
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
private
|
268
|
-
|
269
|
-
def parse_string
|
270
|
-
if scan(STRING)
|
271
|
-
return '' if self[1].empty?
|
272
|
-
self[1].gsub(%r(\\(?:[\\bfnrt"/]|u([A-Fa-f\d]{4})))) do
|
273
|
-
case $~[0]
|
274
|
-
when '\\\\' then '\\'
|
275
|
-
when '\\b' then "\b"
|
276
|
-
when '\\f' then "\f"
|
277
|
-
when '\\n' then "\n"
|
278
|
-
when '\\r' then "\r"
|
279
|
-
when '\\t' then "\t"
|
280
|
-
when '\\"' then '"'
|
281
|
-
when '\\/' then '/'
|
282
|
-
else
|
283
|
-
if JSON.support_unicode? and $KCODE == 'UTF8'
|
284
|
-
JSON.utf16_to_utf8($~[1])
|
285
|
-
else
|
286
|
-
# if utf8 mode is switched off or unicode not supported, try to
|
287
|
-
# transform unicode \u-notation to bytes directly:
|
288
|
-
$~[1].to_i(16).chr
|
289
|
-
end
|
290
|
-
end
|
291
|
-
end
|
292
|
-
else
|
293
|
-
UNPARSED
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
def parse_value
|
298
|
-
case
|
299
|
-
when scan(FLOAT)
|
300
|
-
Float(self[0])
|
301
|
-
when scan(INTEGER)
|
302
|
-
Integer(self[0])
|
303
|
-
when scan(TRUE)
|
304
|
-
true
|
305
|
-
when scan(FALSE)
|
306
|
-
false
|
307
|
-
when scan(NULL)
|
308
|
-
nil
|
309
|
-
when (string = parse_string) != UNPARSED
|
310
|
-
string
|
311
|
-
when scan(ARRAY_OPEN)
|
312
|
-
parse_array
|
313
|
-
when scan(OBJECT_OPEN)
|
314
|
-
parse_object
|
315
|
-
else
|
316
|
-
UNPARSED
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
|
-
def parse_array
|
321
|
-
result = []
|
322
|
-
until eos?
|
323
|
-
case
|
324
|
-
when (value = parse_value) != UNPARSED
|
325
|
-
result << value
|
326
|
-
skip(IGNORE)
|
327
|
-
unless scan(COLLECTION_DELIMITER) or match?(ARRAY_CLOSE)
|
328
|
-
raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
|
329
|
-
end
|
330
|
-
when scan(ARRAY_CLOSE)
|
331
|
-
break
|
332
|
-
when skip(IGNORE)
|
333
|
-
;
|
334
|
-
else
|
335
|
-
raise ParserError, "unexpected token in array at '#{peek(20)}'!"
|
336
|
-
end
|
337
|
-
end
|
338
|
-
result
|
339
|
-
end
|
340
|
-
|
341
|
-
def parse_object
|
342
|
-
result = {}
|
343
|
-
until eos?
|
344
|
-
case
|
345
|
-
when (string = parse_string) != UNPARSED
|
346
|
-
skip(IGNORE)
|
347
|
-
unless scan(PAIR_DELIMITER)
|
348
|
-
raise ParserError, "expected ':' in object at '#{peek(20)}'!"
|
349
|
-
end
|
350
|
-
skip(IGNORE)
|
351
|
-
unless (value = parse_value).equal? UNPARSED
|
352
|
-
result[string] = value
|
353
|
-
skip(IGNORE)
|
354
|
-
unless scan(COLLECTION_DELIMITER) or match?(OBJECT_CLOSE)
|
355
|
-
raise ParserError,
|
356
|
-
"expected ',' or '}' in object at '#{peek(20)}'!"
|
357
|
-
end
|
358
|
-
else
|
359
|
-
raise ParserError, "expected value in object at '#{peek(20)}'!"
|
360
|
-
end
|
361
|
-
when scan(OBJECT_CLOSE)
|
362
|
-
if klassname = result['json_class']
|
363
|
-
klass = klassname.sub(/^:+/, '').split(/::/).inject(Object) do |p,k|
|
364
|
-
p.const_get(k) rescue nil
|
365
|
-
end
|
366
|
-
break unless klass and klass.json_creatable?
|
367
|
-
result = klass.json_create(result)
|
368
|
-
end
|
369
|
-
break
|
370
|
-
when skip(IGNORE)
|
371
|
-
;
|
372
|
-
else
|
373
|
-
raise ParserError, "unexpected token in object at '#{peek(20)}'!"
|
374
|
-
end
|
375
|
-
end
|
376
|
-
result
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
# This class is used to create State instances, that are use to hold data
|
381
|
-
# while unparsing a Ruby data structure into a JSON string.
|
382
|
-
class State
|
383
|
-
# Creates a State object from _opts_, which ought to be Hash to create a
|
384
|
-
# new State instance configured by opts, something else to create an
|
385
|
-
# unconfigured instance. If _opts_ is a State object, it is just returned.
|
386
|
-
def self.from_state(opts)
|
387
|
-
case opts
|
388
|
-
when self
|
389
|
-
opts
|
390
|
-
when Hash
|
391
|
-
new(opts)
|
392
|
-
else
|
393
|
-
new
|
394
|
-
end
|
395
|
-
end
|
396
|
-
|
397
|
-
# Instantiates a new State object, configured by _opts_.
|
398
|
-
def initialize(opts = {})
|
399
|
-
@indent = opts[:indent] || ''
|
400
|
-
@space = opts[:space] || ''
|
401
|
-
@object_nl = opts[:object_nl] || ''
|
402
|
-
@array_nl = opts[:array_nl] || ''
|
403
|
-
@seen = {}
|
404
|
-
end
|
405
|
-
|
406
|
-
# This string is used to indent levels in the JSON string.
|
407
|
-
attr_accessor :indent
|
408
|
-
|
409
|
-
# This string is used to include a space between the tokens in a JSON
|
410
|
-
# string.
|
411
|
-
attr_accessor :space
|
412
|
-
|
413
|
-
# This string is put at the end of a line that holds a JSON object (or
|
414
|
-
# Hash).
|
415
|
-
attr_accessor :object_nl
|
416
|
-
|
417
|
-
# This string is put at the end of a line that holds a JSON array.
|
418
|
-
attr_accessor :array_nl
|
419
|
-
|
420
|
-
# Returns _true_, if _object_ was already seen during this Unparsing run.
|
421
|
-
def seen?(object)
|
422
|
-
@seen.key?(object.__id__)
|
423
|
-
end
|
424
|
-
|
425
|
-
# Remember _object_, to find out if it was already encountered (to find out
|
426
|
-
# if a cyclic data structure is unparsed).
|
427
|
-
def remember(object)
|
428
|
-
@seen[object.__id__] = true
|
429
|
-
end
|
430
|
-
|
431
|
-
# Forget _object_ for this Unparsing run.
|
432
|
-
def forget(object)
|
433
|
-
@seen.delete object.__id__
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
module_function
|
438
|
-
|
439
|
-
# Convert _string_ from UTF8 encoding to UTF16 (big endian) encoding and
|
440
|
-
# return it.
|
441
|
-
def utf8_to_utf16(string)
|
442
|
-
JSON::UTF8toUTF16.iconv(string).unpack('H*')[0]
|
443
|
-
end
|
444
|
-
|
445
|
-
# Convert _string_ from UTF16 (big endian) encoding to UTF8 encoding and
|
446
|
-
# return it.
|
447
|
-
def utf16_to_utf8(string)
|
448
|
-
bytes = '' << string[0, 2].to_i(16) << string[2, 2].to_i(16)
|
449
|
-
JSON::UTF16toUTF8.iconv(bytes)
|
450
|
-
end
|
451
|
-
|
452
|
-
# Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
|
453
|
-
# UTF16 big endian characters as \u????, and return it.
|
454
|
-
def utf8_to_json(string)
|
455
|
-
i, n, result = 0, string.size, ''
|
456
|
-
while i < n
|
457
|
-
char = string[i]
|
458
|
-
case
|
459
|
-
when char == ?\b then result << '\b'
|
460
|
-
when char == ?\t then result << '\t'
|
461
|
-
when char == ?\n then result << '\n'
|
462
|
-
when char == ?\f then result << '\f'
|
463
|
-
when char == ?\r then result << '\r'
|
464
|
-
when char == ?" then result << '\"'
|
465
|
-
when char == ?\\ then result << '\\\\'
|
466
|
-
when char == ?/ then result << '\/'
|
467
|
-
when char.between?(0x0, 0x1f) then result << "\\u%04x" % char
|
468
|
-
when char.between?(0x20, 0x7f) then result << char
|
469
|
-
when !(JSON.support_unicode? && $KCODE == 'UTF8')
|
470
|
-
# if utf8 mode is switched off or unicode not supported, just pass
|
471
|
-
# bytes through:
|
472
|
-
result << char
|
473
|
-
when char & 0xe0 == 0xc0
|
474
|
-
result << '\u' << utf8_to_utf16(string[i, 2])
|
475
|
-
i += 1
|
476
|
-
when char & 0xf0 == 0xe0
|
477
|
-
result << '\u' << utf8_to_utf16(string[i, 3])
|
478
|
-
i += 2
|
479
|
-
when char & 0xf8 == 0xf0
|
480
|
-
result << '\u' << utf8_to_utf16(string[i, 4])
|
481
|
-
i += 3
|
482
|
-
when char & 0xfc == 0xf8
|
483
|
-
result << '\u' << utf8_to_utf16(string[i, 5])
|
484
|
-
i += 4
|
485
|
-
when char & 0xfe == 0xfc
|
486
|
-
result << '\u' << utf8_to_utf16(string[i, 6])
|
487
|
-
i += 5
|
488
|
-
else
|
489
|
-
raise JSON::UnparserError, "Encountered unknown UTF-8 byte: %x!" % char
|
490
|
-
end
|
491
|
-
i += 1
|
492
|
-
end
|
493
|
-
result
|
494
|
-
end
|
495
|
-
|
496
|
-
# Parse the JSON string _source_ into a Ruby data structure and return it.
|
497
|
-
def parse(source)
|
498
|
-
Parser.new(source).parse
|
499
|
-
end
|
500
|
-
|
501
|
-
# Unparse the Ruby data structure _obj_ into a single line JSON string and
|
502
|
-
# return it. _state_ is a JSON::State object, that can be used to configure
|
503
|
-
# the output further.
|
504
|
-
def unparse(obj, state = nil)
|
505
|
-
obj.to_json(JSON::State.from_state(state))
|
506
|
-
end
|
507
|
-
|
508
|
-
# Unparse the Ruby data structure _obj_ into a JSON string and return it.
|
509
|
-
# The returned string is a prettier form of the string returned by #unparse.
|
510
|
-
def pretty_unparse(obj)
|
511
|
-
state = JSON::State.new(
|
512
|
-
:indent => ' ',
|
513
|
-
:space => ' ',
|
514
|
-
:object_nl => "\n",
|
515
|
-
:array_nl => "\n"
|
516
|
-
)
|
517
|
-
obj.to_json(state)
|
518
|
-
end
|
519
|
-
end
|
520
|
-
|
521
|
-
class Object
|
522
|
-
# Converts this object to a string (calling #to_s), converts
|
523
|
-
# it to a JSON string, and returns the result. This is a fallback, if no
|
524
|
-
# special method #to_json was defined for some object.
|
525
|
-
# _state_ is a JSON::State object, that can also be used
|
526
|
-
# to configure the produced JSON string output further.
|
527
|
-
|
528
|
-
def to_json(*) to_s.to_json end
|
529
|
-
end
|
530
|
-
|
531
|
-
class Hash
|
532
|
-
# Returns a JSON string containing a JSON object, that is unparsed from
|
533
|
-
# this Hash instance.
|
534
|
-
# _state_ is a JSON::State object, that can also be used to configure the
|
535
|
-
# produced JSON string output further.
|
536
|
-
# _depth_ is used to find out nesting depth, to indent accordingly.
|
537
|
-
def to_json(state = nil, depth = 0)
|
538
|
-
state = JSON::State.from_state(state)
|
539
|
-
json_check_circular(state) { json_transform(state, depth) }
|
540
|
-
end
|
541
|
-
|
542
|
-
private
|
543
|
-
|
544
|
-
def json_check_circular(state)
|
545
|
-
if state
|
546
|
-
state.seen?(self) and raise JSON::CircularDatastructure,
|
547
|
-
"circular data structures not supported!"
|
548
|
-
state.remember self
|
549
|
-
end
|
550
|
-
yield
|
551
|
-
ensure
|
552
|
-
state and state.forget self
|
553
|
-
end
|
554
|
-
|
555
|
-
def json_shift(state, depth)
|
556
|
-
state and not state.object_nl.empty? or return ''
|
557
|
-
state.indent * depth
|
558
|
-
end
|
559
|
-
|
560
|
-
def json_transform(state, depth)
|
561
|
-
delim = ','
|
562
|
-
delim << state.object_nl if state
|
563
|
-
result = '{'
|
564
|
-
result << state.object_nl if state
|
565
|
-
result << map { |key,value|
|
566
|
-
json_shift(state, depth + 1) <<
|
567
|
-
key.to_s.to_json(state, depth + 1) <<
|
568
|
-
':' << state.space << value.to_json(state, depth + 1)
|
569
|
-
}.join(delim)
|
570
|
-
result << state.object_nl if state
|
571
|
-
result << json_shift(state, depth)
|
572
|
-
result << '}'
|
573
|
-
result
|
574
|
-
end
|
575
|
-
end
|
576
|
-
|
577
|
-
class Array
|
578
|
-
# Returns a JSON string containing a JSON array, that is unparsed from
|
579
|
-
# this Array instance.
|
580
|
-
# _state_ is a JSON::State object, that can also be used to configure the
|
581
|
-
# produced JSON string output further.
|
582
|
-
# _depth_ is used to find out nesting depth, to indent accordingly.
|
583
|
-
def to_json(state = nil, depth = 0)
|
584
|
-
state = JSON::State.from_state(state)
|
585
|
-
json_check_circular(state) { json_transform(state, depth) }
|
586
|
-
end
|
587
|
-
|
588
|
-
private
|
589
|
-
|
590
|
-
def json_check_circular(state)
|
591
|
-
if state
|
592
|
-
state.seen?(self) and raise JSON::CircularDatastructure,
|
593
|
-
"circular data structures not supported!"
|
594
|
-
state.remember self
|
595
|
-
end
|
596
|
-
yield
|
597
|
-
ensure
|
598
|
-
state and state.forget self
|
599
|
-
end
|
600
|
-
|
601
|
-
def json_shift(state, depth)
|
602
|
-
state and not state.array_nl.empty? or return ''
|
603
|
-
state.indent * depth
|
604
|
-
end
|
605
|
-
|
606
|
-
def json_transform(state, depth)
|
607
|
-
delim = ','
|
608
|
-
delim << state.array_nl if state
|
609
|
-
result = '['
|
610
|
-
result << state.array_nl if state
|
611
|
-
result << map { |value|
|
612
|
-
json_shift(state, depth + 1) << value.to_json(state, depth + 1)
|
613
|
-
}.join(delim)
|
614
|
-
result << state.array_nl if state
|
615
|
-
result << json_shift(state, depth)
|
616
|
-
result << ']'
|
617
|
-
result
|
618
|
-
end
|
619
|
-
end
|
620
|
-
|
621
|
-
class Integer
|
622
|
-
# Returns a JSON string representation for this Integer number.
|
623
|
-
def to_json(*) to_s end
|
624
|
-
end
|
625
|
-
|
626
|
-
class Float
|
627
|
-
# Returns a JSON string representation for this Float number.
|
628
|
-
def to_json(*) to_s end
|
629
|
-
end
|
630
|
-
|
631
|
-
class String
|
632
|
-
# This string should be encoded with UTF-8 (if JSON unicode support is
|
633
|
-
# enabled). A call to this method returns a JSON string
|
634
|
-
# encoded with UTF16 big endian characters as \u????. If
|
635
|
-
# JSON.support_unicode? is false only control characters are encoded this
|
636
|
-
# way, all 8-bit bytes are just passed through.
|
637
|
-
def to_json(*)
|
638
|
-
'"' << JSON::utf8_to_json(self) << '"'
|
639
|
-
end
|
640
|
-
|
641
|
-
# Raw Strings are JSON Objects (the raw bytes are stored in an array for the
|
642
|
-
# key "raw"). The Ruby String can be created by this class method.
|
643
|
-
def self.json_create(o)
|
644
|
-
o['raw'].pack('C*')
|
645
|
-
end
|
646
|
-
|
647
|
-
# This method creates a raw object, that can be nested into other data
|
648
|
-
# structures and will be unparsed as a raw string.
|
649
|
-
def to_json_raw_object
|
650
|
-
{
|
651
|
-
'json_class' => self.class.name,
|
652
|
-
'raw' => self.unpack('C*'),
|
653
|
-
}
|
654
|
-
end
|
655
|
-
|
656
|
-
# This method should be used, if you want to convert raw strings to JSON
|
657
|
-
# instead of UTF-8 strings, e. g. binary data (and JSON Unicode support is
|
658
|
-
# enabled).
|
659
|
-
def to_json_raw(*args)
|
660
|
-
to_json_raw_object.to_json(*args)
|
661
|
-
end
|
662
|
-
end
|
663
|
-
|
664
|
-
class TrueClass
|
665
|
-
# Returns a JSON string for true: 'true'.
|
666
|
-
def to_json(*) to_s end
|
667
|
-
end
|
668
|
-
|
669
|
-
class FalseClass
|
670
|
-
# Returns a JSON string for false: 'false'.
|
671
|
-
def to_json(*) to_s end
|
672
|
-
end
|
673
|
-
|
674
|
-
class NilClass
|
675
|
-
# Returns a JSON string for nil: 'null'.
|
676
|
-
def to_json(*) 'null' end
|
677
|
-
end
|
678
|
-
|
679
|
-
module Kernel
|
680
|
-
# Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
|
681
|
-
# one line.
|
682
|
-
def j(*objs)
|
683
|
-
objs.each do |obj|
|
684
|
-
puts JSON::unparse(obj)
|
685
|
-
end
|
686
|
-
nil
|
687
|
-
end
|
688
|
-
|
689
|
-
# Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
|
690
|
-
# indentation and over many lines.
|
691
|
-
def jj(*objs)
|
692
|
-
objs.each do |obj|
|
693
|
-
puts JSON::pretty_unparse(obj)
|
694
|
-
end
|
695
|
-
nil
|
696
|
-
end
|
697
|
-
end
|
698
|
-
|
699
|
-
class Class
|
700
|
-
# Returns true, if this class can be used to create an instance
|
701
|
-
# from a serialised JSON string. The class has to implement a class
|
702
|
-
# method _json_create_ that expects a hash as first parameter, which includes
|
703
|
-
# the required data.
|
704
|
-
def json_creatable?
|
705
|
-
respond_to?(:json_create)
|
706
|
-
end
|
707
|
-
end
|
708
|
-
# vim: set et sw=2 ts=2:
|