blob: 57872bdc7f097242d19ae0d0c67790aac601e070 [file] [log] [blame]
Roger Meier85fb6de2012-11-02 00:05:42 +00001#
2# Licensed to the Apache Software Foundation (ASF) under one
3# or more contributor license agreements. See the NOTICE file
4# distributed with this work for additional information
5# regarding copyright ownership. The ASF licenses this file
6# to you under the Apache License, Version 2.0 (the
7# "License"); you may not use this file except in compliance
8# with the License. You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing,
13# software distributed under the License is distributed on an
14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15# KIND, either express or implied. See the License for the
16# specific language governing permissions and limitations
17# under the License.
18#
19
20from TProtocol import *
21import json, base64, sys
22
23__all__ = ['TJSONProtocol', 'TJSONProtocolFactory']
24
25VERSION = 1
26
27COMMA = ','
28COLON = ':'
29LBRACE = '{'
30RBRACE = '}'
31LBRACKET = '['
32RBRACKET = ']'
33QUOTE = '"'
34BACKSLASH = '\\'
35ZERO = '0'
36
37ESCSEQ = '\\u00'
38ESCAPE_CHAR = '"\\bfnrt'
39ESCAPE_CHAR_VALS = ['"', '\\', '\b', '\f', '\n', '\r', '\t']
40NUMERIC_CHAR = '+-.0123456789Ee'
41
42CTYPES = {TType.BOOL: 'tf',
43 TType.BYTE: 'i8',
44 TType.I16: 'i16',
45 TType.I32: 'i32',
46 TType.I64: 'i64',
47 TType.DOUBLE: 'dbl',
48 TType.STRING: 'str',
Roger Meier05ab89a2012-11-02 10:36:59 +000049 TType.STRUCT: 'rec',
Roger Meier85fb6de2012-11-02 00:05:42 +000050 TType.LIST: 'lst',
51 TType.SET: 'set',
52 TType.MAP: 'map'}
53
54JTYPES = {}
55for key in CTYPES.keys():
56 JTYPES[CTYPES[key]] = key
57
58
59class JSONBaseContext():
60
61 def __init__(self, protocol):
62 self.protocol = protocol
63 self.first = True
64
65 def doIO(self, function):
66 pass
67
68 def write(self):
69 pass
70
71 def read(self):
72 pass
73
74 def escapeNum(self):
75 return False
76
77
78class JSONListContext(JSONBaseContext):
79
80 def doIO(self, function):
81 if self.first is True:
82 self.first = False
83 else:
84 function(COMMA)
85
86 def write(self):
87 self.doIO(self.protocol.trans.write)
88
89 def read(self):
90 self.doIO(self.protocol.readJSONSyntaxChar)
91
92
93class JSONPairContext(JSONBaseContext):
94 colon = True
95
96 def doIO(self, function):
97 if self.first is True:
98 self.first = False
99 self.colon = True
100 else:
101 function(COLON if self.colon == True else COMMA)
102 self.colon = not self.colon
103
104 def write(self):
105 self.doIO(self.protocol.trans.write)
106
107 def read(self):
108 self.doIO(self.protocol.readJSONSyntaxChar)
109
110 def escapeNum(self):
111 return self.colon
112
113
114class LookaheadReader():
115 hasData = False
116 data = ''
117
118 def __init__(self, protocol):
119 self.protocol = protocol
120
121 def read(self):
122 if self.hasData is True:
123 self.hasData = False
124 else:
125 self.data = self.protocol.trans.read(1)
126 return self.data
127
128 def peek(self):
129 if self.hasData is False:
130 self.data = self.protocol.trans.read(1)
131 self.hasData = True
132 return self.data
133
134class TJSONProtocolBase(TProtocolBase):
135
136 def __init__(self, trans):
137 TProtocolBase.__init__(self, trans)
138
139 def resetWriteContext(self):
140 self.contextStack = []
141 self.context = JSONBaseContext(self)
142
143 def resetReadContext(self):
144 self.resetWriteContext()
145 self.reader = LookaheadReader(self)
146
147 def pushContext(self, ctx):
148 self.contextStack.append(ctx)
149 self.context = ctx
150
151 def popContext(self):
152 self.contextStack.pop()
153
154 def writeJSONString(self, string):
155 self.context.write()
156 self.trans.write(json.dumps(string))
157
158 def writeJSONNumber(self, number):
159 self.context.write()
160 jsNumber = str(number)
161 if self.context.escapeNum():
162 jsNumber = "%s%s%s" % (QUOTE, jsNumber, QUOTE)
163 self.trans.write(jsNumber)
164
165 def writeJSONBase64(self, binary):
166 self.context.write()
167 self.trans.write(QUOTE)
168 self.trans.write(base64.b64encode(binary))
169 self.trans.write(QUOTE)
170
171 def writeJSONObjectStart(self):
172 self.context.write()
173 self.trans.write(LBRACE)
174 self.pushContext(JSONPairContext(self))
175
176 def writeJSONObjectEnd(self):
177 self.popContext()
178 self.trans.write(RBRACE)
179
180 def writeJSONArrayStart(self):
181 self.context.write()
182 self.trans.write(LBRACKET)
183 self.pushContext(JSONListContext(self))
184
185 def writeJSONArrayEnd(self):
186 self.popContext()
187 self.trans.write(RBRACKET)
188
189 def readJSONSyntaxChar(self, character):
190 current = self.reader.read()
191 if character != current:
192 raise TProtocolException(TProtocolException.INVALID_DATA,
193 "Unexpected character: %s" % current)
194
195 def readJSONString(self, skipContext):
196 string = []
197 if skipContext is False:
198 self.context.read()
199 self.readJSONSyntaxChar(QUOTE)
200 while True:
201 character = self.reader.read()
202 if character == QUOTE:
203 break
204 if character == ESCSEQ[0]:
205 character = self.reader.read()
206 if character == ESCSEQ[1]:
207 self.readJSONSyntaxChar(ZERO)
208 self.readJSONSyntaxChar(ZERO)
209 character = json.JSONDecoder().decode('"\u00%s"' % self.trans.read(2))
210 else:
211 off = ESCAPE_CHAR.find(char)
212 if off == -1:
213 raise TProtocolException(TProtocolException.INVALID_DATA,
214 "Expected control char")
215 character = ESCAPE_CHAR_VALS[off]
216 string.append(character)
217 return ''.join(string)
218
219 def isJSONNumeric(self, character):
220 return (True if NUMERIC_CHAR.find(character) != - 1 else False)
221
222 def readJSONQuotes(self):
223 if (self.context.escapeNum()):
224 self.readJSONSyntaxChar(QUOTE)
225
226 def readJSONNumericChars(self):
227 numeric = []
228 while True:
229 character = self.reader.peek()
230 if self.isJSONNumeric(character) is False:
231 break
232 numeric.append(self.reader.read())
233 return ''.join(numeric)
234
235 def readJSONInteger(self):
236 self.context.read()
237 self.readJSONQuotes()
238 numeric = self.readJSONNumericChars()
239 self.readJSONQuotes()
240 try:
241 return int(numeric)
242 except ValueError:
243 raise TProtocolException(TProtocolException.INVALID_DATA,
244 "Bad data encounted in numeric data")
245
246 def readJSONDouble(self):
247 self.context.read()
248 if self.reader.peek() == QUOTE:
249 string = self.readJSONString(True)
250 try:
251 double = float(string)
252 if self.context.escapeNum is False and double != inf and double != nan:
253 raise TProtocolException(TProtocolException.INVALID_DATA,
254 "Numeric data unexpectedly quoted")
255 return double
256 except ValueError:
257 raise TProtocolException(TProtocolException.INVALID_DATA,
258 "Bad data encounted in numeric data")
259 else:
260 if self.context.escapeNum() is True:
261 self.readJSONSyntaxChar(QUOTE)
262 try:
263 return float(self.readJSONNumericChars())
264 except ValueErro:
265 raise TProtocolException(TProtocolException.INVALID_DATA,
266 "Bad data encounted in numeric data")
267
268 def readJSONBase64(self):
269 string = self.readJSONString(False)
270 return base64.b64decode(string)
271
272 def readJSONObjectStart(self):
273 self.context.read()
274 self.readJSONSyntaxChar(LBRACE)
275 self.pushContext(JSONPairContext(self))
276
277 def readJSONObjectEnd(self):
278 self.readJSONSyntaxChar(RBRACE)
279 self.popContext()
280
281 def readJSONArrayStart(self):
282 self.context.read()
283 self.readJSONSyntaxChar(LBRACKET)
284 self.pushContext(JSONListContext(self))
285
286 def readJSONArrayEnd(self):
287 self.readJSONSyntaxChar(RBRACKET)
288 self.popContext()
289
290
291class TJSONProtocol(TJSONProtocolBase):
292
293 def readMessageBegin(self):
294 self.resetReadContext()
295 self.readJSONArrayStart()
296 if self.readJSONInteger() != VERSION:
297 raise TProtocolException(TProtocolException.BAD_VERSION,
298 "Message contained bad version.")
299 name = self.readJSONString(False)
300 typen = self.readJSONInteger()
301 seqid = self.readJSONInteger()
302 return (name, typen, seqid)
303
304 def readMessageEnd(self):
305 self.readJSONArrayEnd()
306
307 def readStructBegin(self):
308 self.readJSONObjectStart()
309
310 def readStructEnd(self):
311 self.readJSONObjectEnd()
312
313 def readFieldBegin(self):
314 character = self.reader.peek()
315 type = 0
316 id = 0
317 if character == RBRACE:
318 type = TType.STOP
319 else:
320 id = self.readJSONInteger()
321 self.readJSONObjectStart()
322 type = JTYPES[self.readJSONString(False)]
323 return (None, type, id)
324
325 def readFieldEnd(self):
326 self.readJSONObjectEnd()
327
328 def readMapBegin(self):
329 self.readJSONArrayStart()
330 keyType = JTYPES[self.readJSONString(False)]
331 valueType = JTYPES[self.readJSONString(False)]
332 size = self.readJSONInteger()
333 self.readJSONObjectStart()
334 return (keyType, valueType, size)
335
336 def readMapEnd(self):
337 self.readJSONObjectEnd()
338 self.readJSONArrayEnd()
339
340 def readCollectionBegin(self):
341 self.readJSONArrayStart()
342 elemType = JTYPES[self.readJSONString(False)]
343 size = self.readJSONInteger()
344 return (type, size)
345 readListBegin = readCollectionBegin
346 readSetBegin = readCollectionBegin
347
348 def readCollectionEnd(self):
349 self.readJSONArrayEnd()
350 readSetEnd = readCollectionEnd
351 readListEnd = readCollectionEnd
352
353 def readBool(self):
354 return (False if self.readJSONInteger() == 0 else True)
355
356 def readNumber(self):
357 return self.readJSONInteger()
358 readByte = readNumber
359 readI16 = readNumber
360 readI32 = readNumber
361 readI64 = readNumber
362
363 def readDouble(self):
364 return self.readJSONDouble()
365
366 def readString(self):
367 return self.readJSONString(False)
368
369 def readBinary(self):
370 return self.readJSONBase64()
371
372 def writeMessageBegin(self, name, request_type, seqid):
373 self.resetWriteContext()
374 self.writeJSONArrayStart()
375 self.writeJSONNumber(VERSION)
376 self.writeJSONString(name)
377 self.writeJSONNumber(request_type)
378 self.writeJSONNumber(seqid)
379
380 def writeMessageEnd(self):
381 self.writeJSONArrayEnd()
382
383 def writeStructBegin(self, name):
384 self.writeJSONObjectStart()
385
386 def writeStructEnd(self):
387 self.writeJSONObjectEnd()
388
389 def writeFieldBegin(self, name, type, id):
390 self.writeJSONNumber(id)
391 self.writeJSONObjectStart()
392 self.writeJSONString(CTYPES[type])
393
394 def writeFieldEnd(self):
395 self.writeJSONObjectEnd()
396
397 def writeFieldStop(self):
398 pass
399
400 def writeMapBegin(self, ktype, vtype, size):
401 self.writeJSONArrayStart()
402 self.writeJSONString(CTYPES[ktype])
403 self.writeJSONString(CTYPES[vtype])
404 self.writeJSONNumber(size)
405 self.writeJSONObjectStart()
406
407 def writeMapEnd(self):
408 self.writeJSONObjectEnd()
409 self.writeJSONArrayEnd()
410
411 def writeListBegin(self, etype, size):
412 self.writeJSONArrayStart()
413 self.writeJSONString(CTYPES[etype])
414 self.writeJSONNumber(size)
415
416 def writeListEnd(self):
417 self.writeJSONArrayEnd()
418
419 def writeSetBegin(self, etype, size):
420 self.writeJSONArrayStart()
421 self.writeJSONString(CTYPES[etype])
422 self.writeJSONNumber(size)
423
424 def writeSetEnd(self):
425 self.writeJSONArrayEnd()
426
427 def writeBool(self, boolean):
428 self.writeJSONNumber(1 if boolean is True else 0)
429
430 def writeInteger(self, integer):
431 self.writeJSONNumber(integer)
432 writeByte = writeInteger
433 writeI16 = writeInteger
434 writeI32 = writeInteger
435 writeI64 = writeInteger
436
437 def writeDouble(self, dbl):
438 self.writeJSONNumber(dbl)
439
440 def writeString(self, string):
441 self.writeJSONString(string)
442
443 def writeBinary(self, binary):
444 self.writeJSONBase64(binary)
445
446class TJSONProtocolFactory:
447 def __init__(self):
448 pass
449
450 def getProtocol(self, trans):
451 return TJSONProtocol(trans)