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
|
module JSON
def self.dump(object, io=nil, limit=nil)
state = {}
state[:max_nesting] = limit if limit
begin
js = JSON.generate(object, nil, state)
rescue JSON::NestingError
raise ArgumentError, "exceeded depth limit"
end
if io
io.write js
io
else
js
end
end
def self.generate(obj, options=nil, state=nil)
options = (options || {}).to_hash unless options.is_a? Hash
options[:pretty_print] ||= false
options[:indent_with] ||= 2
state = (state || {}).to_hash unless state.is_a? Hash
state[:max_nesting] ||= 100
state[:nesting] = 0
self.generate0(obj, options, state)
end
def self.generate0(obj, options, state)
if state[:nesting] >= state[:max_nesting]
raise JSON::NestingError, "nesting of #{state[:nesting]} is too deep"
end
pretty = options[:pretty_print]
if pretty
indent = options[:indent_with].class == Fixnum ? " " * options[:indent_with] : options[:indent_with]
else
indent = ""
end
nl = pretty ? "\n" : ""
if obj == false
return "false"
elsif obj == nil
return "null"
elsif obj == true
return "true"
elsif obj.is_a? Hash
members = []
state[:nesting] += 1
obj.each { |k, v|
members << JSON.generate0(k, options, state) + ":" + (pretty ? " " : "") + JSON.generate0(v, options, state)
}
if pretty
members.map! { |k| (indent * state[:nesting]) + "#{k}" }.join("_")
end
state[:nesting] -= 1
return "{" + nl + members.join("," + nl) + nl + (indent * state[:nesting]) + "}"
elsif obj.is_a? Array
state[:nesting] += 1
members = obj.map { |v| JSON.generate0(v, options, state) }
if pretty
members.map! { |k| (indent * state[:nesting]) + "#{k}" }.join("_")
end
state[:nesting] -= 1
return "[" + nl + members.join("," + nl) + nl + (indent * state[:nesting]) + "]"
elsif obj.is_a? Fixnum
return obj.to_s
elsif obj.is_a? Float
if obj.infinite? or obj.nan?
raise GeneratorError, "#{obj.to_s} not allowed in JSON"
end
sprintf "%.17g", obj
else
a = []
obj.to_s.each_char { |ch|
a << if ch < "\x20"
case ch
when "\x08"
"\\b"
when "\x0c"
"\\f"
when "\x0a"
"\\n"
when "\x0d"
"\\r"
when "\x09"
"\\t"
else
raise GeneratorError, "cannot convert #{ch.inspect} to JSON"
end
elsif ch == '"'
'\\"'
elsif ch == '\\'
"\\\\"
else
ch
end
}
return '"' + a.join + '"'
end
end
def self.load(source) # TODO: proc, options
source = source.read unless source.is_a? String
JSON.parse source
end
class JSONError < StandardError; end
class GeneratorError < JSONError; end
class ParserError < JSONError; end
class NestingError < ParserError; end
end
unless Float.method_defined? :nan?
class Float
def nan?
not (self == self)
end
end
end
|