~ubuntu-branches/ubuntu/oneiric/weave/oneiric

« back to all changes in this revision

Viewing changes to tools/scripts/json.py

  • Committer: Bazaar Package Importer
  • Author(s): Micah Gersten
  • Date: 2010-08-11 00:35:15 UTC
  • mfrom: (3.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20100811003515-o3jbh826bnd1syjv
Tags: 1.4.3-1ubuntu1
* Add -fshort-wchar to CXXFLAGS to fix FTBFS in Ubuntu
  - update debian/rules 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
import string
2
 
import types
3
 
 
4
 
##    json.py implements a JSON (http://json.org) reader and writer.
5
 
##    Copyright (C) 2005  Patrick D. Logan
6
 
##    Contact mailto:patrickdlogan@stardecisions.com
7
 
##
8
 
##    This library is free software; you can redistribute it and/or
9
 
##    modify it under the terms of the GNU Lesser General Public
10
 
##    License as published by the Free Software Foundation; either
11
 
##    version 2.1 of the License, or (at your option) any later version.
12
 
##
13
 
##    This library is distributed in the hope that it will be useful,
14
 
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 
##    Lesser General Public License for more details.
17
 
##
18
 
##    You should have received a copy of the GNU Lesser General Public
19
 
##    License along with this library; if not, write to the Free Software
20
 
##    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
 
 
22
 
 
23
 
class _StringGenerator(object):
24
 
        def __init__(self, string):
25
 
                self.string = string
26
 
                self.index = -1
27
 
        def peek(self):
28
 
                i = self.index + 1
29
 
                if i < len(self.string):
30
 
                        return self.string[i]
31
 
                else:
32
 
                        return None
33
 
        def next(self):
34
 
                self.index += 1
35
 
                if self.index < len(self.string):
36
 
                        return self.string[self.index]
37
 
                else:
38
 
                        raise StopIteration
39
 
        def all(self):
40
 
                return self.string
41
 
 
42
 
class WriteException(Exception):
43
 
    pass
44
 
 
45
 
class ReadException(Exception):
46
 
    pass
47
 
 
48
 
class JsonReader(object):
49
 
    hex_digits = {'A': 10,'B': 11,'C': 12,'D': 13,'E': 14,'F':15}
50
 
    escapes = {'t':'\t','n':'\n','f':'\f','r':'\r','b':'\b'}
51
 
 
52
 
    def read(self, s):
53
 
        self._generator = _StringGenerator(s)
54
 
        result = self._read()
55
 
        return result
56
 
 
57
 
    def _read(self):
58
 
        self._eatWhitespace()
59
 
        peek = self._peek()
60
 
        if peek is None:
61
 
            raise ReadException, "Nothing to read: '%s'" % self._generator.all()
62
 
        if peek == '{':
63
 
            return self._readObject()
64
 
        elif peek == '[':
65
 
            return self._readArray()            
66
 
        elif peek == '"':
67
 
            return self._readString()
68
 
        elif peek == '-' or peek.isdigit():
69
 
            return self._readNumber()
70
 
        elif peek == 't':
71
 
            return self._readTrue()
72
 
        elif peek == 'f':
73
 
            return self._readFalse()
74
 
        elif peek == 'n':
75
 
            return self._readNull()
76
 
        elif peek == '/':
77
 
            self._readComment()
78
 
            return self._read()
79
 
        else:
80
 
            raise ReadException, "Input is not valid JSON: '%s'" % self._generator.all()
81
 
 
82
 
    def _readTrue(self):
83
 
        self._assertNext('t', "true")
84
 
        self._assertNext('r', "true")
85
 
        self._assertNext('u', "true")
86
 
        self._assertNext('e', "true")
87
 
        return True
88
 
 
89
 
    def _readFalse(self):
90
 
        self._assertNext('f', "false")
91
 
        self._assertNext('a', "false")
92
 
        self._assertNext('l', "false")
93
 
        self._assertNext('s', "false")
94
 
        self._assertNext('e', "false")
95
 
        return False
96
 
 
97
 
    def _readNull(self):
98
 
        self._assertNext('n', "null")
99
 
        self._assertNext('u', "null")
100
 
        self._assertNext('l', "null")
101
 
        self._assertNext('l', "null")
102
 
        return None
103
 
 
104
 
    def _assertNext(self, ch, target):
105
 
        if self._next() != ch:
106
 
            raise ReadException, "Trying to read %s: '%s'" % (target, self._generator.all())
107
 
 
108
 
    def _readNumber(self):
109
 
        isfloat = False
110
 
        result = self._next()
111
 
        peek = self._peek()
112
 
        while peek is not None and (peek.isdigit() or peek == "."):
113
 
            isfloat = isfloat or peek == "."
114
 
            result = result + self._next()
115
 
            peek = self._peek()
116
 
        try:
117
 
            if isfloat:
118
 
                return float(result)
119
 
            else:
120
 
                return int(result)
121
 
        except ValueError:
122
 
            raise ReadException, "Not a valid JSON number: '%s'" % result
123
 
 
124
 
    def _readString(self):
125
 
        result = ""
126
 
        assert self._next() == '"'
127
 
        try:
128
 
            while self._peek() != '"':
129
 
                ch = self._next()
130
 
                if ch == "\\":
131
 
                    ch = self._next()
132
 
                    if ch in 'brnft':
133
 
                        ch = self.escapes[ch]
134
 
                    elif ch == "u":
135
 
                        ch4096 = self._next()
136
 
                        ch256  = self._next()
137
 
                        ch16   = self._next()
138
 
                        ch1    = self._next()
139
 
                        n = 4096 * self._hexDigitToInt(ch4096)
140
 
                        n += 256 * self._hexDigitToInt(ch256)
141
 
                        n += 16  * self._hexDigitToInt(ch16)
142
 
                        n += self._hexDigitToInt(ch1)
143
 
                        ch = unichr(n)
144
 
                    elif ch not in '"/\\':
145
 
                        raise ReadException, "Not a valid escaped JSON character: '%s' in %s" % (ch, self._generator.all())
146
 
                result = result + ch
147
 
        except StopIteration:
148
 
            raise ReadException, "Not a valid JSON string: '%s'" % self._generator.all()
149
 
        assert self._next() == '"'
150
 
        return result
151
 
 
152
 
    def _hexDigitToInt(self, ch):
153
 
        try:
154
 
            result = self.hex_digits[ch.upper()]
155
 
        except KeyError:
156
 
            try:
157
 
                result = int(ch)
158
 
            except ValueError:
159
 
                 raise ReadException, "The character %s is not a hex digit." % ch
160
 
        return result
161
 
 
162
 
    def _readComment(self):
163
 
        assert self._next() == "/"
164
 
        second = self._next()
165
 
        if second == "/":
166
 
            self._readDoubleSolidusComment()
167
 
        elif second == '*':
168
 
            self._readCStyleComment()
169
 
        else:
170
 
            raise ReadException, "Not a valid JSON comment: %s" % self._generator.all()
171
 
 
172
 
    def _readCStyleComment(self):
173
 
        try:
174
 
            done = False
175
 
            while not done:
176
 
                ch = self._next()
177
 
                done = (ch == "*" and self._peek() == "/")
178
 
                if not done and ch == "/" and self._peek() == "*":
179
 
                    raise ReadException, "Not a valid JSON comment: %s, '/*' cannot be embedded in the comment." % self._generator.all()
180
 
            self._next()
181
 
        except StopIteration:
182
 
            raise ReadException, "Not a valid JSON comment: %s, expected */" % self._generator.all()
183
 
 
184
 
    def _readDoubleSolidusComment(self):
185
 
        try:
186
 
            ch = self._next()
187
 
            while ch != "\r" and ch != "\n":
188
 
                ch = self._next()
189
 
        except StopIteration:
190
 
            pass
191
 
 
192
 
    def _readArray(self):
193
 
        result = []
194
 
        assert self._next() == '['
195
 
        done = self._peek() == ']'
196
 
        while not done:
197
 
            item = self._read()
198
 
            result.append(item)
199
 
            self._eatWhitespace()
200
 
            done = self._peek() == ']'
201
 
            if not done:
202
 
                ch = self._next()
203
 
                if ch != ",":
204
 
                    raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
205
 
        assert ']' == self._next()
206
 
        return result
207
 
 
208
 
    def _readObject(self):
209
 
        result = {}
210
 
        assert self._next() == '{'
211
 
        done = self._peek() == '}'
212
 
        while not done:
213
 
            key = self._read()
214
 
            if type(key) is not types.StringType:
215
 
                raise ReadException, "Not a valid JSON object key (should be a string): %s" % key
216
 
            self._eatWhitespace()
217
 
            ch = self._next()
218
 
            if ch != ":":
219
 
                raise ReadException, "Not a valid JSON object: '%s' due to: '%s'" % (self._generator.all(), ch)
220
 
            self._eatWhitespace()
221
 
            val = self._read()
222
 
            result[key] = val
223
 
            self._eatWhitespace()
224
 
            done = self._peek() == '}'
225
 
            if not done:
226
 
                ch = self._next()
227
 
                if ch != ",":
228
 
                    raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
229
 
        assert self._next() == "}"
230
 
        return result
231
 
 
232
 
    def _eatWhitespace(self):
233
 
        p = self._peek()
234
 
        while p is not None and p in string.whitespace or p == '/':
235
 
            if p == '/':
236
 
                self._readComment()
237
 
            else:
238
 
                self._next()
239
 
            p = self._peek()
240
 
 
241
 
    def _peek(self):
242
 
        return self._generator.peek()
243
 
 
244
 
    def _next(self):
245
 
        return self._generator.next()
246
 
 
247
 
class JsonWriter(object):
248
 
        
249
 
    def _append(self, s):
250
 
        self._results.append(s)
251
 
 
252
 
    def write(self, obj, escaped_forward_slash=False):
253
 
        self._escaped_forward_slash = escaped_forward_slash
254
 
        self._results = []
255
 
        self._write(obj)
256
 
        return "".join(self._results)
257
 
 
258
 
    def _write(self, obj):
259
 
        ty = type(obj)
260
 
        if ty is types.DictType:
261
 
            n = len(obj)
262
 
            self._append("{")
263
 
            for k, v in obj.items():
264
 
                self._write(k)
265
 
                self._append(":")
266
 
                self._write(v)
267
 
                n = n - 1
268
 
                if n > 0:
269
 
                    self._append(",")
270
 
            self._append("}")
271
 
        elif ty is types.ListType or ty is types.TupleType:
272
 
            n = len(obj)
273
 
            self._append("[")
274
 
            for item in obj:
275
 
                self._write(item)
276
 
                n = n - 1
277
 
                if n > 0:
278
 
                    self._append(",")
279
 
            self._append("]")
280
 
        elif ty is types.StringType or ty is types.UnicodeType:
281
 
            self._append('"')
282
 
            obj = obj.replace('\\', r'\\')
283
 
            if self._escaped_forward_slash:
284
 
                obj = obj.replace('/', r'\/')
285
 
            obj = obj.replace('"', r'\"')
286
 
            obj = obj.replace('\b', r'\b')
287
 
            obj = obj.replace('\f', r'\f')
288
 
            obj = obj.replace('\n', r'\n')
289
 
            obj = obj.replace('\r', r'\r')
290
 
            obj = obj.replace('\t', r'\t')
291
 
            self._append(obj)
292
 
            self._append('"')
293
 
        elif ty is types.IntType or ty is types.LongType:
294
 
            self._append(str(obj))
295
 
        elif ty is types.FloatType:
296
 
            self._append("%f" % obj)
297
 
        elif obj is True:
298
 
            self._append("true")
299
 
        elif obj is False:
300
 
            self._append("false")
301
 
        elif obj is None:
302
 
            self._append("null")
303
 
        else:
304
 
            raise WriteException, "Cannot write in JSON: %s" % repr(obj)
305
 
 
306
 
def write(obj, escaped_forward_slash=False):
307
 
    return JsonWriter().write(obj, escaped_forward_slash)
308
 
 
309
 
def read(s):
310
 
    return JsonReader().read(s)