root / trunk / pyopencoin / oc / json.py

Revision 84, 10.0 kB (checked in by ocjhb, 4 years ago)

added json to pyopencoin

  • Property svn:mime-type set to text/plain
  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1import string
2import 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
23class _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
42class WriteException(Exception):
43    pass
44
45class ReadException(Exception):
46    pass
47
48class 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
247class 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(",\n")
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
306def write(obj, escaped_forward_slash=False):
307    return JsonWriter().write(obj, escaped_forward_slash)
308
309def read(s):
310    return JsonReader().read(s)
Note: See TracBrowser for help on using the browser.