json 0.4.2 → 0.4.3
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 +12 -0
- data/README +9 -1
- data/Rakefile +6 -10
- data/VERSION +1 -1
- data/bla.json.tmp +0 -0
- data/lib/json.rb +27 -17
- data/lib/json.rb.orig +708 -0
- data/lib/json/editor.rb +12 -9
- data/tests/runner.rb +1 -1
- data/tests/test_json.rb +89 -32
- metadata +6 -3
data/CHANGES
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
2007-02-09 (0.4.3)
|
2
|
+
* Conform more to RFC4627 for JSON: This means JSON strings
|
3
|
+
now always must contain exactly one object "{ ... }" or array "[ ... ]" in
|
4
|
+
order to be parsed without raising an exception. The definition of what
|
5
|
+
constitutes a whitespace is narrower in JSON than in Ruby ([ \t\r\n]), and
|
6
|
+
there are differences in floats and integers (no octals or hexadecimals) as
|
7
|
+
well.
|
8
|
+
* Added aliases generate and pretty_generate of unparse and pretty_unparse.
|
9
|
+
* Fixed a test case.
|
10
|
+
* Catch an Іconv::InvalidEncoding exception, that seems to occur on some Sun
|
11
|
+
boxes with SunOS 5.8, if iconv doesn't support utf16 conversions. This was
|
12
|
+
reported by Andrew R Jackson <andrewj@bcm.tmc.edu>, thanks a bunch!
|
1
13
|
2006-08-25 (0.4.2)
|
2
14
|
* Fixed a bug in handling solidi (/-characters), that was reported by
|
3
15
|
Kevin Gilpin <kevin.gilpin@alum.mit.edu>.
|
data/README
CHANGED
@@ -5,6 +5,10 @@ Just type into the command line as root:
|
|
5
5
|
|
6
6
|
# ruby install.rb
|
7
7
|
|
8
|
+
or
|
9
|
+
|
10
|
+
# rake install
|
11
|
+
|
8
12
|
Testing and Examples
|
9
13
|
====================
|
10
14
|
|
@@ -12,6 +16,10 @@ To run the tests type:
|
|
12
16
|
|
13
17
|
$ ruby -I lib tests/runner.rb
|
14
18
|
|
19
|
+
or
|
20
|
+
|
21
|
+
$ rake test
|
22
|
+
|
15
23
|
To get an idea how this library is used also look at the tests (until I have
|
16
24
|
time to better document it.)
|
17
25
|
|
@@ -23,5 +31,5 @@ Florian Frank <flori@ping.de>
|
|
23
31
|
License
|
24
32
|
=======
|
25
33
|
|
26
|
-
GNU General Public License (GPL)
|
34
|
+
GNU General Public License (GPL), Version 2
|
27
35
|
|
data/Rakefile
CHANGED
@@ -5,19 +5,11 @@ include Config
|
|
5
5
|
|
6
6
|
PKG_NAME = 'json'
|
7
7
|
PKG_VERSION = File.read('VERSION').chomp
|
8
|
-
PKG_FILES = FileList["**/*"].exclude(
|
8
|
+
PKG_FILES = FileList["**/*"].exclude(/CVS|pkg|coverage/)
|
9
9
|
|
10
10
|
desc "Installing library"
|
11
11
|
task :install do
|
12
|
-
|
13
|
-
install('lib/json.rb', dest)
|
14
|
-
dest = File.join(dest, 'json')
|
15
|
-
mkdir_p dest
|
16
|
-
Dir['lib/json/*.*'].each do |f|
|
17
|
-
install(f, dest)
|
18
|
-
end
|
19
|
-
dest = CONFIG["bindir"]
|
20
|
-
install('bin/edit_json.rb', dest)
|
12
|
+
ruby 'install.rb'
|
21
13
|
end
|
22
14
|
|
23
15
|
desc "Testing library"
|
@@ -25,6 +17,10 @@ task :test do
|
|
25
17
|
ruby 'tests/runner.rb'
|
26
18
|
end
|
27
19
|
|
20
|
+
desc "Testing library with coverage"
|
21
|
+
task :coverage do
|
22
|
+
system 'rcov -x tests -Ilib tests/runner.rb'
|
23
|
+
end
|
28
24
|
desc "Removing generated files"
|
29
25
|
task :clean do
|
30
26
|
rm_rf 'doc'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.3
|
data/bla.json.tmp
ADDED
File without changes
|
data/lib/json.rb
CHANGED
@@ -25,7 +25,7 @@
|
|
25
25
|
# == Examples
|
26
26
|
#
|
27
27
|
# To create a JSON string from a ruby data structure, you
|
28
|
-
# can call JSON.unparse like that:
|
28
|
+
# can call JSON.unparse (or JSON.generate) like that:
|
29
29
|
#
|
30
30
|
# json = JSON.unparse [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
31
31
|
# # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
|
@@ -86,7 +86,8 @@
|
|
86
86
|
# JSON.unparse always creates the shortes possible string representation of a
|
87
87
|
# ruby data structure in one line. This good for data storage or network
|
88
88
|
# protocols, but not so good for humans to read. Fortunately there's
|
89
|
-
# also JSON.pretty_unparse that creates a more
|
89
|
+
# also JSON.pretty_unparse (or JSON.pretty_generate) that creates a more
|
90
|
+
# readable output:
|
90
91
|
#
|
91
92
|
# puts JSON.pretty_unparse([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
|
92
93
|
# [
|
@@ -157,7 +158,9 @@ module JSON
|
|
157
158
|
UTF16toUTF8 = Iconv.new('utf-8', 'utf-16be')
|
158
159
|
# An iconv instance to convert from UTF16 Big Endian to UTF8.
|
159
160
|
UTF8toUTF16 = Iconv.new('utf-16be', 'utf-8'); UTF8toUTF16.iconv('no bom')
|
160
|
-
rescue Errno::EINVAL
|
161
|
+
rescue Errno::EINVAL, Iconv::InvalidEncoding
|
162
|
+
# Iconv doesn't support big endian utf-16. Let's try to hack this manually
|
163
|
+
# into the converters.
|
161
164
|
begin
|
162
165
|
old_verbose = $VERBOSE
|
163
166
|
$VERBOSE = nil
|
@@ -191,7 +194,7 @@ module JSON
|
|
191
194
|
end
|
192
195
|
UTF16toUTF8 = swapper.new(UTF16toUTF8)
|
193
196
|
end
|
194
|
-
rescue Errno::EINVAL
|
197
|
+
rescue Errno::EINVAL, Iconv::InvalidEncoding
|
195
198
|
# Enforce disabling of unicode support, if iconv doesn't support
|
196
199
|
# UTF8/UTF16 at all.
|
197
200
|
JSON.support_unicode = false
|
@@ -216,8 +219,8 @@ module JSON
|
|
216
219
|
# into a Ruby data structure.
|
217
220
|
class Parser < StringScanner
|
218
221
|
STRING = /"((?:[^"\\]|\\.)*)"/
|
219
|
-
INTEGER =
|
220
|
-
FLOAT =
|
222
|
+
INTEGER = /-?(?:0|[1-9]\d*)/
|
223
|
+
FLOAT = /-?(?:0|[1-9]\d*)\.(\d+)(?i:e[+-]?\d+)?/
|
221
224
|
OBJECT_OPEN = /\{/
|
222
225
|
OBJECT_CLOSE = /\}/
|
223
226
|
ARRAY_OPEN = /\[/
|
@@ -238,7 +241,7 @@ module JSON
|
|
238
241
|
/(?=\*/) # single slash before this comment's end
|
239
242
|
)*
|
240
243
|
\*/ # the end of this comment
|
241
|
-
|
244
|
+
|[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr
|
242
245
|
)+
|
243
246
|
)mx
|
244
247
|
|
@@ -248,20 +251,23 @@ module JSON
|
|
248
251
|
# as a result.
|
249
252
|
def parse
|
250
253
|
reset
|
254
|
+
obj = nil
|
251
255
|
until eos?
|
252
256
|
case
|
253
|
-
when scan(ARRAY_OPEN)
|
254
|
-
return parse_array
|
255
257
|
when scan(OBJECT_OPEN)
|
256
|
-
|
258
|
+
obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
|
259
|
+
obj = parse_object
|
260
|
+
when scan(ARRAY_OPEN)
|
261
|
+
obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
|
262
|
+
obj = parse_array
|
257
263
|
when skip(IGNORE)
|
258
264
|
;
|
259
|
-
when !((value = parse_value).equal? UNPARSED)
|
260
|
-
return value
|
261
265
|
else
|
262
266
|
raise ParserError, "source '#{peek(20)}' not in JSON!"
|
263
267
|
end
|
264
268
|
end
|
269
|
+
obj or raise ParserError, "source did not contain any JSON!"
|
270
|
+
obj
|
265
271
|
end
|
266
272
|
|
267
273
|
private
|
@@ -271,14 +277,14 @@ module JSON
|
|
271
277
|
return '' if self[1].empty?
|
272
278
|
self[1].gsub(%r(\\(?:[\\bfnrt"/]|u([A-Fa-f\d]{4})))) do
|
273
279
|
case $~[0]
|
280
|
+
when '\\"' then '"'
|
274
281
|
when '\\\\' then '\\'
|
282
|
+
when '\\/' then '/'
|
275
283
|
when '\\b' then "\b"
|
276
284
|
when '\\f' then "\f"
|
277
285
|
when '\\n' then "\n"
|
278
286
|
when '\\r' then "\r"
|
279
287
|
when '\\t' then "\t"
|
280
|
-
when '\\"' then '"'
|
281
|
-
when '\\/' then '/'
|
282
288
|
else
|
283
289
|
if JSON.support_unicode? and $KCODE == 'UTF8'
|
284
290
|
JSON.utf16_to_utf8($~[1])
|
@@ -297,7 +303,7 @@ module JSON
|
|
297
303
|
def parse_value
|
298
304
|
case
|
299
305
|
when scan(FLOAT)
|
300
|
-
Float(self[0])
|
306
|
+
Float(self[0].sub(/\.([eE])/, '.0\1'))
|
301
307
|
when scan(INTEGER)
|
302
308
|
Integer(self[0])
|
303
309
|
when scan(TRUE)
|
@@ -505,6 +511,8 @@ module JSON
|
|
505
511
|
obj.to_json(JSON::State.from_state(state))
|
506
512
|
end
|
507
513
|
|
514
|
+
alias generate unparse
|
515
|
+
|
508
516
|
# Unparse the Ruby data structure _obj_ into a JSON string and return it.
|
509
517
|
# The returned string is a prettier form of the string returned by #unparse.
|
510
518
|
def pretty_unparse(obj)
|
@@ -516,6 +524,8 @@ module JSON
|
|
516
524
|
)
|
517
525
|
obj.to_json(state)
|
518
526
|
end
|
527
|
+
|
528
|
+
alias pretty_generate pretty_unparse
|
519
529
|
end
|
520
530
|
|
521
531
|
class Object
|
@@ -681,7 +691,7 @@ module Kernel
|
|
681
691
|
# one line.
|
682
692
|
def j(*objs)
|
683
693
|
objs.each do |obj|
|
684
|
-
puts JSON::
|
694
|
+
puts JSON::generate(obj)
|
685
695
|
end
|
686
696
|
nil
|
687
697
|
end
|
@@ -690,7 +700,7 @@ module Kernel
|
|
690
700
|
# indentation and over many lines.
|
691
701
|
def jj(*objs)
|
692
702
|
objs.each do |obj|
|
693
|
-
puts JSON::
|
703
|
+
puts JSON::pretty_generate(obj)
|
694
704
|
end
|
695
705
|
nil
|
696
706
|
end
|
data/lib/json.rb.orig
ADDED
@@ -0,0 +1,708 @@
|
|
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:
|
data/lib/json/editor.rb
CHANGED
@@ -16,8 +16,11 @@ module JSON
|
|
16
16
|
# Columns constants
|
17
17
|
ICON_COL, TYPE_COL, CONTENT_COL = 0, 1, 2
|
18
18
|
|
19
|
+
# JSON primitive types (Containers)
|
20
|
+
CONTAINER_TYPES = %w[Array Hash].sort
|
19
21
|
# All JSON primitive types
|
20
|
-
ALL_TYPES = %w[TrueClass FalseClass Numeric String
|
22
|
+
ALL_TYPES = (%w[TrueClass FalseClass Numeric String NilClass] +
|
23
|
+
CONTAINER_TYPES).sort
|
21
24
|
|
22
25
|
# The Nodes necessary for the tree representation of a JSON document
|
23
26
|
ALL_NODES = (ALL_TYPES + %w[Key]).sort
|
@@ -849,7 +852,8 @@ module JSON
|
|
849
852
|
hbox.add(Label.new("Type:"))
|
850
853
|
hbox.pack_start(type_input = ComboBox.new(true))
|
851
854
|
default_active = 0
|
852
|
-
ALL_TYPES
|
855
|
+
types = parent ? ALL_TYPES : CONTAINER_TYPES
|
856
|
+
types.each_with_index do |t, i|
|
853
857
|
type_input.append_text(t)
|
854
858
|
if t == default_type
|
855
859
|
default_active = i
|
@@ -858,21 +862,21 @@ module JSON
|
|
858
862
|
type_input.active = default_active
|
859
863
|
dialog.vbox.add(hbox)
|
860
864
|
type_input.signal_connect(:changed) do
|
861
|
-
configure_value(value_input,
|
865
|
+
configure_value(value_input, types[type_input.active])
|
862
866
|
end
|
863
867
|
|
864
868
|
hbox = HBox.new(false, 5)
|
865
869
|
hbox.add(Label.new("Value:"))
|
866
870
|
hbox.pack_start(value_input = Entry.new)
|
867
871
|
value_input.text = value_text if value_text
|
868
|
-
configure_value(value_input,
|
872
|
+
configure_value(value_input, types[type_input.active])
|
869
873
|
|
870
874
|
dialog.vbox.add(hbox)
|
871
875
|
|
872
876
|
dialog.show_all
|
873
877
|
dialog.run do |response|
|
874
878
|
if response == Dialog::RESPONSE_ACCEPT
|
875
|
-
type =
|
879
|
+
type = types[type_input.active]
|
876
880
|
@content = case type
|
877
881
|
when 'Numeric'
|
878
882
|
Integer(value_input.text) rescue Float(value_input.text) rescue 0
|
@@ -1102,12 +1106,11 @@ module JSON
|
|
1102
1106
|
if path
|
1103
1107
|
data = Editor.model2data(@treeview.model.iter_first)
|
1104
1108
|
File.open(path + '.tmp', 'wb') do |output|
|
1105
|
-
|
1106
|
-
JSON.
|
1109
|
+
if @options_menu.pretty_item.active?
|
1110
|
+
output.puts JSON.pretty_generate(data)
|
1107
1111
|
else
|
1108
|
-
JSON.unparse(data)
|
1112
|
+
output.write JSON.unparse(data)
|
1109
1113
|
end
|
1110
|
-
output.write json
|
1111
1114
|
end
|
1112
1115
|
File.rename path + '.tmp', path
|
1113
1116
|
@filename = path
|
data/tests/runner.rb
CHANGED
data/tests/test_json.rb
CHANGED
@@ -31,9 +31,13 @@ class TC_JSON < Test::Unit::TestCase
|
|
31
31
|
|
32
32
|
def setup
|
33
33
|
$KCODE = 'UTF8'
|
34
|
-
@ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true]
|
34
|
+
@ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true].map do
|
35
|
+
|x| [x]
|
36
|
+
end
|
35
37
|
@ary_to_parse = ["1", '"foo"', "3.14", "4711.0", "2.718", "null",
|
36
|
-
"[1,-2,3]", "false", "true"]
|
38
|
+
"[1,-2,3]", "false", "true"].map do
|
39
|
+
|x| "[#{x}]"
|
40
|
+
end
|
37
41
|
@hash = {
|
38
42
|
'a' => 2,
|
39
43
|
'b' => 3.141,
|
@@ -48,15 +52,33 @@ class TC_JSON < Test::Unit::TestCase
|
|
48
52
|
'"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}'
|
49
53
|
@json2 = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
|
50
54
|
'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
|
55
|
+
@json3 = <<'EOT'.chomp
|
56
|
+
{
|
57
|
+
"a": 2,
|
58
|
+
"b": 3.141,
|
59
|
+
"c": "c",
|
60
|
+
"d": [
|
61
|
+
1,
|
62
|
+
"b",
|
63
|
+
3.14
|
64
|
+
],
|
65
|
+
"e": {
|
66
|
+
"foo": "bar"
|
67
|
+
},
|
68
|
+
"g": "\"\u0000\u001f",
|
69
|
+
"h": 1000.0,
|
70
|
+
"i": 0.001
|
71
|
+
}
|
72
|
+
EOT
|
51
73
|
end
|
52
74
|
|
53
75
|
def test_parse_value
|
54
|
-
assert_equal("", parse('""'))
|
55
|
-
assert_equal("\\", parse('"\\\\"'))
|
56
|
-
assert_equal('"', parse('"\""'))
|
57
|
-
assert_equal('\\"\\', parse('"\\\\\\"\\\\"'))
|
58
|
-
assert_equal("\\a\"\b\f\n\r\t\0\037",
|
59
|
-
parse('"\\a\"\b\f\n\r\t\u0000\u001f"'))
|
76
|
+
assert_equal([""], parse('[""]'))
|
77
|
+
assert_equal(["\\"], parse('["\\\\"]'))
|
78
|
+
assert_equal(['"'], parse('["\""]'))
|
79
|
+
assert_equal(['\\"\\'], parse('["\\\\\\"\\\\"]'))
|
80
|
+
assert_equal(["\\a\"\b\f\n\r\t\0\037"],
|
81
|
+
parse('["\\a\"\b\f\n\r\t\u0000\u001f"]'))
|
60
82
|
for i in 0 ... @ary.size
|
61
83
|
assert_equal(@ary[i], parse(@ary_to_parse[i]))
|
62
84
|
end
|
@@ -68,9 +90,10 @@ class TC_JSON < Test::Unit::TestCase
|
|
68
90
|
assert_equal([1], parse('[1]'))
|
69
91
|
assert_equal([1], parse(' [ 1 ] '))
|
70
92
|
assert_equal(@ary,
|
71
|
-
parse('[1,"foo",3.14,47.11e+2,2718.
|
72
|
-
|
73
|
-
|
93
|
+
parse('[[1],["foo"],[3.14],[47.11e+2],[2718.0E-3],[null],[[1,-2,3]]'\
|
94
|
+
',[false],[true]]'))
|
95
|
+
assert_equal(@ary, parse(%Q{ [ [1] , ["foo"] , [3.14] \t , [47.11e+2]
|
96
|
+
, [2718.0E-3 ],\n[ null] , [[1, -2, 3 ]], [false ],[ true]\n ] }))
|
74
97
|
end
|
75
98
|
|
76
99
|
def test_parse_object
|
@@ -85,12 +108,28 @@ class TC_JSON < Test::Unit::TestCase
|
|
85
108
|
assert_equal(@json2, json)
|
86
109
|
parsed_json = parse(json)
|
87
110
|
assert_equal(@hash, parsed_json)
|
88
|
-
json =
|
111
|
+
json = generate({1=>2})
|
89
112
|
assert_equal('{"1":2}', json)
|
90
113
|
parsed_json = parse(json)
|
91
114
|
assert_equal({"1"=>2}, parsed_json)
|
92
115
|
end
|
93
116
|
|
117
|
+
def test_unparse
|
118
|
+
json = pretty_unparse(@hash)
|
119
|
+
assert_equal(@json3, json)
|
120
|
+
parsed_json = parse(json)
|
121
|
+
assert_equal(@hash, parsed_json)
|
122
|
+
json = pretty_generate({1=>2})
|
123
|
+
assert_equal(<<'EOT'.chomp, json)
|
124
|
+
{
|
125
|
+
"1": 2
|
126
|
+
}
|
127
|
+
EOT
|
128
|
+
parsed_json = parse(json)
|
129
|
+
assert_equal({"1"=>2}, parsed_json)
|
130
|
+
end
|
131
|
+
|
132
|
+
|
94
133
|
def test_parser_reset
|
95
134
|
parser = Parser.new(@json)
|
96
135
|
assert_equal(@hash, parser.parse)
|
@@ -105,16 +144,16 @@ class TC_JSON < Test::Unit::TestCase
|
|
105
144
|
assert_equal '" "', ' '.to_json
|
106
145
|
assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
|
107
146
|
if JSON.support_unicode?
|
108
|
-
utf8 = '© ≠ €!'
|
109
|
-
json = '"\u00a9 \u2260 \u20ac!"'
|
147
|
+
utf8 = [ '© ≠ €!' ]
|
148
|
+
json = '["\u00a9 \u2260 \u20ac!"]'
|
110
149
|
assert_equal json, utf8.to_json
|
111
150
|
assert_equal utf8, parse(json)
|
112
|
-
utf8 = "\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"
|
113
|
-
json = '"\u3042\u3044\u3046\u3048\u304a"'
|
151
|
+
utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
|
152
|
+
json = '["\u3042\u3044\u3046\u3048\u304a"]'
|
114
153
|
assert_equal json, utf8.to_json
|
115
154
|
assert_equal utf8, parse(json)
|
116
|
-
utf8 = 'საქართველო'
|
117
|
-
json = '"\u10e1\u10d0\u10e5\u10d0\u10e0\u10d7\u10d5\u10d4\u10da\u10dd"'
|
155
|
+
utf8 = ['საქართველო']
|
156
|
+
json = '["\u10e1\u10d0\u10e5\u10d0\u10e0\u10d7\u10d5\u10d4\u10da\u10dd"]'
|
118
157
|
assert_equal json, utf8.to_json
|
119
158
|
assert_equal utf8, parse(json)
|
120
159
|
else
|
@@ -164,7 +203,7 @@ EOT
|
|
164
203
|
|
165
204
|
def test_extended_json
|
166
205
|
a = A.new(666)
|
167
|
-
json = a
|
206
|
+
json = generate(a)
|
168
207
|
a_again = JSON.parse(json)
|
169
208
|
assert_kind_of a.class, a_again
|
170
209
|
assert_equal a, a_again
|
@@ -196,35 +235,53 @@ EOT
|
|
196
235
|
return
|
197
236
|
end
|
198
237
|
$KCODE = 'NONE'
|
199
|
-
utf8 = "© ≠ €! - \001"
|
200
|
-
json = "\"© ≠ €! - \\u0001\""
|
201
|
-
assert_equal json, utf8
|
238
|
+
utf8 = ["© ≠ €! - \001"]
|
239
|
+
json = "[\"© ≠ €! - \\u0001\"]"
|
240
|
+
assert_equal json, generate(utf8)
|
202
241
|
assert_equal utf8, parse(json)
|
203
242
|
assert JSON.support_unicode?
|
204
243
|
$KCODE = 'UTF8'
|
205
|
-
utf8 = '© ≠ €!'
|
206
|
-
json = '"\u00a9 \u2260 \u20ac!"'
|
207
|
-
assert_equal json, utf8
|
244
|
+
utf8 = ['© ≠ €!']
|
245
|
+
json = '["\u00a9 \u2260 \u20ac!"]'
|
246
|
+
assert_equal json, unparse(utf8)
|
208
247
|
assert_equal utf8, parse(json)
|
209
248
|
JSON.support_unicode = false
|
210
249
|
assert !JSON.support_unicode?
|
211
|
-
utf8 = "© ≠ €! - \001"
|
212
|
-
json = "\"© ≠ €! - \\u0001\""
|
213
|
-
assert_equal json, utf8
|
250
|
+
utf8 = ["© ≠ €! - \001"]
|
251
|
+
json = "[\"© ≠ €! - \\u0001\"]"
|
252
|
+
assert_equal json, unparse(utf8)
|
214
253
|
assert_equal utf8, parse(json)
|
215
254
|
end
|
216
255
|
|
217
256
|
def test_backslash
|
218
|
-
json = '"\\\\.(?i:gif|jpe?g|png)$"'
|
257
|
+
json = '["\\\\.(?i:gif|jpe?g|png)$"]'
|
219
258
|
data = JSON.parse(json)
|
220
259
|
assert_equal json, JSON.unparse(data)
|
221
|
-
json = '"\\""'
|
260
|
+
json = '["\\""]'
|
222
261
|
data = JSON.parse(json)
|
223
262
|
assert_equal json, JSON.unparse(data)
|
224
|
-
json = '"\/"'
|
263
|
+
json = '["\/"]'
|
225
264
|
data = JSON.parse(json)
|
226
|
-
assert_equal '/', data
|
265
|
+
assert_equal ['/'], data
|
227
266
|
assert_equal json, JSON.unparse(data)
|
228
267
|
end
|
268
|
+
|
269
|
+
def test_wrong_inputs
|
270
|
+
assert_raises(ParserError) { JSON.parse('"foo"') }
|
271
|
+
assert_raises(ParserError) { JSON.parse('123') }
|
272
|
+
assert_raises(ParserError) { JSON.parse('[] bla') }
|
273
|
+
assert_raises(ParserError) { JSON.parse('[] 1') }
|
274
|
+
assert_raises(ParserError) { JSON.parse('[] []') }
|
275
|
+
assert_raises(ParserError) { JSON.parse('[] {}') }
|
276
|
+
assert_raises(ParserError) { JSON.parse('{} []') }
|
277
|
+
assert_raises(ParserError) { JSON.parse('{} {}') }
|
278
|
+
assert_raises(ParserError) { JSON.parse('[NULL]') }
|
279
|
+
assert_raises(ParserError) { JSON.parse('[FALSE]') }
|
280
|
+
assert_raises(ParserError) { JSON.parse('[TRUE]') }
|
281
|
+
assert_raises(ParserError) { JSON.parse('[07] ') }
|
282
|
+
assert_raises(ParserError) { JSON.parse('[0a]') }
|
283
|
+
assert_raises(ParserError) { JSON.parse('[1.]') }
|
284
|
+
assert_raises(ParserError) { JSON.parse(' ') }
|
285
|
+
end
|
229
286
|
end
|
230
287
|
# vim: set et sw=2 ts=2:
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.
|
2
|
+
rubygems_version: 0.9.0
|
3
3
|
specification_version: 1
|
4
4
|
name: json
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.4.
|
7
|
-
date:
|
6
|
+
version: 0.4.3
|
7
|
+
date: 2007-02-10 00:00:00 +01:00
|
8
8
|
summary: A JSON implementation in Ruby
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -25,6 +25,7 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
25
25
|
platform: ruby
|
26
26
|
signing_key:
|
27
27
|
cert_chain:
|
28
|
+
post_install_message:
|
28
29
|
authors:
|
29
30
|
- Florian Frank
|
30
31
|
files:
|
@@ -35,12 +36,14 @@ files:
|
|
35
36
|
- GPL
|
36
37
|
- install.rb
|
37
38
|
- Rakefile
|
39
|
+
- bla.json.tmp
|
38
40
|
- lib
|
39
41
|
- README
|
40
42
|
- CHANGES
|
41
43
|
- bin/edit_json.rb
|
42
44
|
- tests/runner.rb
|
43
45
|
- tests/test_json.rb
|
46
|
+
- lib/json.rb.orig
|
44
47
|
- lib/json
|
45
48
|
- lib/json.rb
|
46
49
|
- lib/json/FalseClass.xpm
|