fjson 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +5 -0
- data/GPL +340 -0
- data/README +28 -0
- data/Rakefile +83 -0
- data/TODO +0 -0
- data/VERSION +1 -0
- data/lib/extensions/array_ext.so +0 -0
- data/lib/extensions/class.rb +9 -0
- data/lib/extensions/false_class_ext.so +0 -0
- data/lib/extensions/float_ext.so +0 -0
- data/lib/extensions/hash_ext.so +0 -0
- data/lib/extensions/integer_ext.so +0 -0
- data/lib/extensions/kernel.rb +19 -0
- data/lib/extensions/nil_class_ext.so +0 -0
- data/lib/extensions/object_ext.so +0 -0
- data/lib/extensions/string.rb +23 -0
- data/lib/extensions/string_ext.so +0 -0
- data/lib/extensions/true_class_ext.so +0 -0
- data/lib/fjson.rb +190 -0
- data/lib/json/editor.rb +1195 -0
- data/lib/json_ext.so +0 -0
- data/lib/parser.rb +165 -0
- data/lib/state_ext.so +0 -0
- data/spec/array_spec.rb +36 -0
- data/spec/false_class_spec.rb +15 -0
- data/spec/float_spec.rb +15 -0
- data/spec/hash_spec.rb +37 -0
- data/spec/integer_spec.rb +15 -0
- data/spec/nil_class_spec.rb +15 -0
- data/spec/object_spec.rb +17 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/spec_suite.rb +5 -0
- data/spec/state_spec.rb +67 -0
- data/spec/string_spec.rb +15 -0
- data/spec/string_when_not_supporting_unicode_and_kcode_is_not_utf8_json_spec.rb +18 -0
- data/spec/string_when_supporting_unicode_and_kcode_is_not_utf8_json_spec.rb +18 -0
- data/spec/string_with_latin1_character_set_json_spec.rb +54 -0
- data/spec/string_with_utf8_values_when_supporting_unicode_json_spec.rb +36 -0
- data/spec/true_class_spec.rb +15 -0
- metadata +97 -0
data/lib/json_ext.so
ADDED
Binary file
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
module JSON
|
2
|
+
# This class implements the JSON parser that is used to parse a JSON string
|
3
|
+
# into a Ruby data structure.
|
4
|
+
class Parser < StringScanner
|
5
|
+
STRING = /"((?:[^"\\]|\\.)*)"/
|
6
|
+
INTEGER = /-?\d+/
|
7
|
+
FLOAT = /-?\d+\.(\d*)(?i:e[+-]?\d+)?/
|
8
|
+
OBJECT_OPEN = /\{/
|
9
|
+
OBJECT_CLOSE = /\}/
|
10
|
+
ARRAY_OPEN = /\[/
|
11
|
+
ARRAY_CLOSE = /\]/
|
12
|
+
PAIR_DELIMITER = /:/
|
13
|
+
COLLECTION_DELIMITER = /,/
|
14
|
+
TRUE = /true/
|
15
|
+
FALSE = /false/
|
16
|
+
NULL = /null/
|
17
|
+
IGNORE = %r(
|
18
|
+
(?:
|
19
|
+
//[^\n\r]*[\n\r]| # line comments
|
20
|
+
/\* # c-style comments
|
21
|
+
(?:
|
22
|
+
[^*/]| # normal chars
|
23
|
+
/[^*]| # slashes that do not start a nested comment
|
24
|
+
\*[^/]| # asterisks that do not end this comment
|
25
|
+
/(?=\*/) # single slash before this comment's end
|
26
|
+
)*
|
27
|
+
\*/ # the end of this comment
|
28
|
+
|\s+ # whitespaces
|
29
|
+
)+
|
30
|
+
)mx
|
31
|
+
|
32
|
+
UNPARSED = Object.new
|
33
|
+
|
34
|
+
# Parses the current JSON string and returns the complete data structure
|
35
|
+
# as a result.
|
36
|
+
def parse
|
37
|
+
reset
|
38
|
+
until eos?
|
39
|
+
case
|
40
|
+
when scan(ARRAY_OPEN)
|
41
|
+
return parse_array
|
42
|
+
when scan(OBJECT_OPEN)
|
43
|
+
return parse_object
|
44
|
+
when skip(IGNORE)
|
45
|
+
;
|
46
|
+
when !((value = parse_value).equal? UNPARSED)
|
47
|
+
return value
|
48
|
+
else
|
49
|
+
raise ParserError, "source '#{peek(20)}' not in JSON!"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def parse_string
|
57
|
+
if scan(STRING)
|
58
|
+
return '' if self[1].empty?
|
59
|
+
self[1].gsub(/\\(?:[\\bfnrt"]|u([A-Fa-f\d]{4}))/) do
|
60
|
+
case $~[0]
|
61
|
+
when '\\\\' then '\\'
|
62
|
+
when '\\b' then "\b"
|
63
|
+
when '\\f' then "\f"
|
64
|
+
when '\\n' then "\n"
|
65
|
+
when '\\r' then "\r"
|
66
|
+
when '\\t' then "\t"
|
67
|
+
when '\\"' then '"'
|
68
|
+
else
|
69
|
+
if JSON.support_unicode? and $KCODE == 'UTF8'
|
70
|
+
JSON.utf16_to_utf8($~[1])
|
71
|
+
else
|
72
|
+
# if utf8 mode is switched off or unicode not supported, try to
|
73
|
+
# transform unicode \u-notation to bytes directly:
|
74
|
+
$~[1].to_i(16).chr
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
else
|
79
|
+
UNPARSED
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def parse_value
|
84
|
+
case
|
85
|
+
when scan(FLOAT)
|
86
|
+
Float(self[0])
|
87
|
+
when scan(INTEGER)
|
88
|
+
Integer(self[0])
|
89
|
+
when scan(TRUE)
|
90
|
+
true
|
91
|
+
when scan(FALSE)
|
92
|
+
false
|
93
|
+
when scan(NULL)
|
94
|
+
nil
|
95
|
+
when (string = parse_string) != UNPARSED
|
96
|
+
string
|
97
|
+
when scan(ARRAY_OPEN)
|
98
|
+
parse_array
|
99
|
+
when scan(OBJECT_OPEN)
|
100
|
+
parse_object
|
101
|
+
else
|
102
|
+
UNPARSED
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_array
|
107
|
+
result = []
|
108
|
+
until eos?
|
109
|
+
case
|
110
|
+
when (value = parse_value) != UNPARSED
|
111
|
+
result << value
|
112
|
+
skip(IGNORE)
|
113
|
+
unless scan(COLLECTION_DELIMITER) or match?(ARRAY_CLOSE)
|
114
|
+
raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
|
115
|
+
end
|
116
|
+
when scan(ARRAY_CLOSE)
|
117
|
+
break
|
118
|
+
when skip(IGNORE)
|
119
|
+
;
|
120
|
+
else
|
121
|
+
raise ParserError, "unexpected token in array at '#{peek(20)}'!"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
result
|
125
|
+
end
|
126
|
+
|
127
|
+
def parse_object
|
128
|
+
result = {}
|
129
|
+
until eos?
|
130
|
+
case
|
131
|
+
when (string = parse_string) != UNPARSED
|
132
|
+
skip(IGNORE)
|
133
|
+
unless scan(PAIR_DELIMITER)
|
134
|
+
raise ParserError, "expected ':' in object at '#{peek(20)}'!"
|
135
|
+
end
|
136
|
+
skip(IGNORE)
|
137
|
+
unless (value = parse_value).equal? UNPARSED
|
138
|
+
result[string] = value
|
139
|
+
skip(IGNORE)
|
140
|
+
unless scan(COLLECTION_DELIMITER) or match?(OBJECT_CLOSE)
|
141
|
+
raise ParserError,
|
142
|
+
"expected ',' or '}' in object at '#{peek(20)}'!"
|
143
|
+
end
|
144
|
+
else
|
145
|
+
raise ParserError, "expected value in object at '#{peek(20)}'!"
|
146
|
+
end
|
147
|
+
when scan(OBJECT_CLOSE)
|
148
|
+
if klassname = result['json_class']
|
149
|
+
klass = klassname.sub(/^:+/, '').split(/::/).inject(Object) do |p,k|
|
150
|
+
p.const_get(k) rescue nil
|
151
|
+
end
|
152
|
+
break unless klass and klass.json_creatable?
|
153
|
+
result = klass.json_create(result)
|
154
|
+
end
|
155
|
+
break
|
156
|
+
when skip(IGNORE)
|
157
|
+
;
|
158
|
+
else
|
159
|
+
raise ParserError, "unexpected token in object at '#{peek(20)}'!"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
result
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
data/lib/state_ext.so
ADDED
Binary file
|
data/spec/array_spec.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A one dimensional Array" do
|
8
|
+
specify "should create json" do
|
9
|
+
array = [1, 'hello', :hello]
|
10
|
+
array.to_json.should_equal '[1,"hello","hello"]'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "A multi-dimensional Array" do
|
15
|
+
specify "should create json" do
|
16
|
+
array = [1, ['hello', [:hello]]]
|
17
|
+
array.to_json.should_equal '[1,["hello",["hello"]]]'
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "should not support circular dependencies" do
|
21
|
+
array = []
|
22
|
+
array << 1
|
23
|
+
array << ['hello', array]
|
24
|
+
proc {array.to_json}.should_raise JSON::CircularDatastructure
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#context "Array performance" do
|
29
|
+
# specify "" do
|
30
|
+
# begin_time = Time.now
|
31
|
+
# 100000.times do
|
32
|
+
# [1, ['hello', [:hello]]].to_json
|
33
|
+
# end
|
34
|
+
# puts "#{Time.now - begin_time} seconds"
|
35
|
+
# end
|
36
|
+
#end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A false object" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
false.to_json.should_equal "false"
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
false.to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|
data/spec/float_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A float number" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
(3.14).to_json.should_equal "3.14"
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
(3.14).to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|
data/spec/hash_spec.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A one dimensional Hash" do
|
8
|
+
specify "should create json" do
|
9
|
+
hash = {
|
10
|
+
:one => 1,
|
11
|
+
"hello" => :hello
|
12
|
+
}
|
13
|
+
json = hash.to_json
|
14
|
+
json.should_include "\"one\":1"
|
15
|
+
json.should_include "\"hello\":\"hello\""
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "A multi-dimensional Hash" do
|
20
|
+
specify "should create json" do
|
21
|
+
hash = {
|
22
|
+
:one => 1,
|
23
|
+
"hello" => {
|
24
|
+
:hello2 => 2
|
25
|
+
},
|
26
|
+
}
|
27
|
+
json = hash.to_json
|
28
|
+
json.should_include "\"one\":1"
|
29
|
+
json.should_include "\"hello\":{\"hello2\":2}"
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "should not support circular dependencies" do
|
33
|
+
hash = { :one => 1 }
|
34
|
+
hash["hello"] = hash
|
35
|
+
proc {hash.to_json}.should_raise JSON::CircularDatastructure
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "An integer" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
5.to_json.should_equal "5"
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
5.to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A nil object" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
nil.to_json.should_equal "null"
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
nil.to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|
data/spec/object_spec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "An object" do
|
8
|
+
specify "should convert its string representation to json" do
|
9
|
+
obj = Object.new
|
10
|
+
obj_str = obj.to_s
|
11
|
+
obj.to_json.should_equal '"' + obj_str + '"'
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "to_json should accept any number of arguments" do
|
15
|
+
Object.new.to_json(1, 2)
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec'
|
2
|
+
dir = File.dirname(__FILE__)
|
3
|
+
args = ARGV.dup
|
4
|
+
|
5
|
+
class Object
|
6
|
+
def metaclass
|
7
|
+
class << self; self; end
|
8
|
+
end
|
9
|
+
|
10
|
+
def define_instance_method(sym, &block)
|
11
|
+
metaclass.__send__(:define_method, sym, &block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
unless args.include?("-f") || args.include?("--format")
|
16
|
+
args << "--format"
|
17
|
+
args << "specdoc"
|
18
|
+
end
|
19
|
+
args << $0
|
20
|
+
|
21
|
+
$context_runner = ::Spec::Runner::OptionParser.create_context_runner(args, false, STDERR, STDOUT)
|
22
|
+
|
23
|
+
at_exit do
|
24
|
+
unless context_runner.instance_eval {@reporter}.instance_eval {@start_time}
|
25
|
+
context_runner.run(false)
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_suite.rb
ADDED
data/spec/state_spec.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A JSON state" do
|
8
|
+
specify "method from_state should return the passed in object when a state is passed in" do
|
9
|
+
state = JSON::State.new
|
10
|
+
JSON::State.from_state(state).should_equal state
|
11
|
+
end
|
12
|
+
|
13
|
+
specify "method from_state should return a new state with hash values when a hash is passed in" do
|
14
|
+
shim_state_class = Object.new
|
15
|
+
shim_state_class.extend JSON::State::ClassMethods
|
16
|
+
passed_params = nil
|
17
|
+
shim_state_class.define_instance_method(:new) {|params| passed_params = params}
|
18
|
+
|
19
|
+
args = {}
|
20
|
+
shim_state_class.from_state(args)
|
21
|
+
passed_params.should_equal args
|
22
|
+
end
|
23
|
+
|
24
|
+
specify "method from_state should return a new state with default parameters when an object that is not " +
|
25
|
+
"a state or a hash is passed in" do
|
26
|
+
state = JSON::State.from_state(Object.new)
|
27
|
+
state.should_be_instance_of JSON::State
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "should initialize its attributes to their defaults" do
|
31
|
+
state = JSON::State.new
|
32
|
+
state.indent.should_equal ""
|
33
|
+
state.space.should_equal ""
|
34
|
+
state.object_nl.should_equal ""
|
35
|
+
state.array_nl.should_equal ""
|
36
|
+
state.instance_eval {@seen}.should_equal({})
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "should initialize its attributes with passed in values" do
|
40
|
+
params = {
|
41
|
+
:indent => Object.new,
|
42
|
+
:space => Object.new,
|
43
|
+
:object_nl => Object.new,
|
44
|
+
:array_nl => Object.new
|
45
|
+
}
|
46
|
+
state = JSON::State.new(params)
|
47
|
+
state.indent.should_equal params[:indent]
|
48
|
+
state.space.should_equal params[:space]
|
49
|
+
state.object_nl.should_equal params[:object_nl]
|
50
|
+
state.array_nl.should_equal params[:array_nl]
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "should remember objects" do
|
54
|
+
state = JSON::State.new
|
55
|
+
obj = Object.new
|
56
|
+
state.remember(obj)
|
57
|
+
state.should_be_seen(obj)
|
58
|
+
end
|
59
|
+
|
60
|
+
specify "should forget objects" do
|
61
|
+
state = JSON::State.new
|
62
|
+
obj = Object.new
|
63
|
+
state.remember(obj)
|
64
|
+
state.forget(obj)
|
65
|
+
state.should_not_be_seen(obj)
|
66
|
+
end
|
67
|
+
end
|
data/spec/string_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A string" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
"test".to_json.should_equal '"test"'
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
"test".to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|