blob: 5fb3ec7788d910595de3f0a76b1141bac33334ba [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
Roger Meierad8154a2012-12-18 21:02:16 +010059class JSONBaseContext(object):
Roger Meier85fb6de2012-11-02 00:05:42 +000060
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)
Roger Meierad8154a2012-12-18 21:02:16 +0100138 self.resetWriteContext()
139 self.resetReadContext()
Roger Meier85fb6de2012-11-02 00:05:42 +0000140
141 def resetWriteContext(self):
142 self.contextStack = []
143 self.context = JSONBaseContext(self)
144
145 def resetReadContext(self):
146 self.resetWriteContext()
147 self.reader = LookaheadReader(self)
148
149 def pushContext(self, ctx):
150 self.contextStack.append(ctx)
151 self.context = ctx
152
153 def popContext(self):
154 self.contextStack.pop()
155
156 def writeJSONString(self, string):
157 self.context.write()
158 self.trans.write(json.dumps(string))
159
160 def writeJSONNumber(self, number):
161 self.context.write()
162 jsNumber = str(number)
163 if self.context.escapeNum():
164 jsNumber = "%s%s%s" % (QUOTE, jsNumber, QUOTE)
165 self.trans.write(jsNumber)
166
167 def writeJSONBase64(self, binary):
168 self.context.write()
169 self.trans.write(QUOTE)
170 self.trans.write(base64.b64encode(binary))
171 self.trans.write(QUOTE)
172
173 def writeJSONObjectStart(self):
174 self.context.write()
175 self.trans.write(LBRACE)
176 self.pushContext(JSONPairContext(self))
177
178 def writeJSONObjectEnd(self):
179 self.popContext()
180 self.trans.write(RBRACE)
181
182 def writeJSONArrayStart(self):
183 self.context.write()
184 self.trans.write(LBRACKET)
185 self.pushContext(JSONListContext(self))
186
187 def writeJSONArrayEnd(self):
188 self.popContext()
189 self.trans.write(RBRACKET)
190
191 def readJSONSyntaxChar(self, character):
192 current = self.reader.read()
193 if character != current:
194 raise TProtocolException(TProtocolException.INVALID_DATA,
195 "Unexpected character: %s" % current)
196
197 def readJSONString(self, skipContext):
198 string = []
199 if skipContext is False:
200 self.context.read()
201 self.readJSONSyntaxChar(QUOTE)
202 while True:
203 character = self.reader.read()
204 if character == QUOTE:
205 break
206 if character == ESCSEQ[0]:
207 character = self.reader.read()
208 if character == ESCSEQ[1]:
209 self.readJSONSyntaxChar(ZERO)
210 self.readJSONSyntaxChar(ZERO)
211 character = json.JSONDecoder().decode('"\u00%s"' % self.trans.read(2))
212 else:
213 off = ESCAPE_CHAR.find(char)
214 if off == -1:
215 raise TProtocolException(TProtocolException.INVALID_DATA,
216 "Expected control char")
217 character = ESCAPE_CHAR_VALS[off]
218 string.append(character)
219 return ''.join(string)
220
221 def isJSONNumeric(self, character):
222 return (True if NUMERIC_CHAR.find(character) != - 1 else False)
223
224 def readJSONQuotes(self):
225 if (self.context.escapeNum()):
226 self.readJSONSyntaxChar(QUOTE)
227
228 def readJSONNumericChars(self):
229 numeric = []
230 while True:
231 character = self.reader.peek()
232 if self.isJSONNumeric(character) is False:
233 break
234 numeric.append(self.reader.read())
235 return ''.join(numeric)
236
237 def readJSONInteger(self):
238 self.context.read()
239 self.readJSONQuotes()
240 numeric = self.readJSONNumericChars()
241 self.readJSONQuotes()
242 try:
243 return int(numeric)
244 except ValueError:
245 raise TProtocolException(TProtocolException.INVALID_DATA,
246 "Bad data encounted in numeric data")
247
248 def readJSONDouble(self):
249 self.context.read()
250 if self.reader.peek() == QUOTE:
251 string = self.readJSONString(True)
252 try:
253 double = float(string)
254 if self.context.escapeNum is False and double != inf and double != nan:
255 raise TProtocolException(TProtocolException.INVALID_DATA,
256 "Numeric data unexpectedly quoted")
257 return double
258 except ValueError:
259 raise TProtocolException(TProtocolException.INVALID_DATA,
260 "Bad data encounted in numeric data")
261 else:
262 if self.context.escapeNum() is True:
263 self.readJSONSyntaxChar(QUOTE)
264 try:
265 return float(self.readJSONNumericChars())
Roger Meierad8154a2012-12-18 21:02:16 +0100266 except ValueError:
Roger Meier85fb6de2012-11-02 00:05:42 +0000267 raise TProtocolException(TProtocolException.INVALID_DATA,
268 "Bad data encounted in numeric data")
269
270 def readJSONBase64(self):
271 string = self.readJSONString(False)
272 return base64.b64decode(string)
273
274 def readJSONObjectStart(self):
275 self.context.read()
276 self.readJSONSyntaxChar(LBRACE)
277 self.pushContext(JSONPairContext(self))
278
279 def readJSONObjectEnd(self):
280 self.readJSONSyntaxChar(RBRACE)
281 self.popContext()
282
283 def readJSONArrayStart(self):
284 self.context.read()
285 self.readJSONSyntaxChar(LBRACKET)
286 self.pushContext(JSONListContext(self))
287
288 def readJSONArrayEnd(self):
289 self.readJSONSyntaxChar(RBRACKET)
290 self.popContext()
291
292
293class TJSONProtocol(TJSONProtocolBase):
294
295 def readMessageBegin(self):
296 self.resetReadContext()
297 self.readJSONArrayStart()
298 if self.readJSONInteger() != VERSION:
299 raise TProtocolException(TProtocolException.BAD_VERSION,
300 "Message contained bad version.")
301 name = self.readJSONString(False)
302 typen = self.readJSONInteger()
303 seqid = self.readJSONInteger()
304 return (name, typen, seqid)
305
306 def readMessageEnd(self):
307 self.readJSONArrayEnd()
308
309 def readStructBegin(self):
310 self.readJSONObjectStart()
311
312 def readStructEnd(self):
313 self.readJSONObjectEnd()
314
315 def readFieldBegin(self):
316 character = self.reader.peek()
Roger Meierad8154a2012-12-18 21:02:16 +0100317 ttype = 0
Roger Meier85fb6de2012-11-02 00:05:42 +0000318 id = 0
319 if character == RBRACE:
Roger Meierad8154a2012-12-18 21:02:16 +0100320 ttype = TType.STOP
Roger Meier85fb6de2012-11-02 00:05:42 +0000321 else:
322 id = self.readJSONInteger()
323 self.readJSONObjectStart()
Roger Meierad8154a2012-12-18 21:02:16 +0100324 ttype = JTYPES[self.readJSONString(False)]
325 return (None, ttype, id)
Roger Meier85fb6de2012-11-02 00:05:42 +0000326
327 def readFieldEnd(self):
328 self.readJSONObjectEnd()
329
330 def readMapBegin(self):
331 self.readJSONArrayStart()
332 keyType = JTYPES[self.readJSONString(False)]
333 valueType = JTYPES[self.readJSONString(False)]
334 size = self.readJSONInteger()
335 self.readJSONObjectStart()
336 return (keyType, valueType, size)
337
338 def readMapEnd(self):
339 self.readJSONObjectEnd()
340 self.readJSONArrayEnd()
341
342 def readCollectionBegin(self):
343 self.readJSONArrayStart()
344 elemType = JTYPES[self.readJSONString(False)]
345 size = self.readJSONInteger()
Roger Meierad8154a2012-12-18 21:02:16 +0100346 return (elemType, size)
Roger Meier85fb6de2012-11-02 00:05:42 +0000347 readListBegin = readCollectionBegin
348 readSetBegin = readCollectionBegin
349
350 def readCollectionEnd(self):
351 self.readJSONArrayEnd()
352 readSetEnd = readCollectionEnd
353 readListEnd = readCollectionEnd
354
355 def readBool(self):
356 return (False if self.readJSONInteger() == 0 else True)
357
358 def readNumber(self):
359 return self.readJSONInteger()
360 readByte = readNumber
361 readI16 = readNumber
362 readI32 = readNumber
363 readI64 = readNumber
364
365 def readDouble(self):
366 return self.readJSONDouble()
367
368 def readString(self):
369 return self.readJSONString(False)
370
371 def readBinary(self):
372 return self.readJSONBase64()
373
374 def writeMessageBegin(self, name, request_type, seqid):
375 self.resetWriteContext()
376 self.writeJSONArrayStart()
377 self.writeJSONNumber(VERSION)
378 self.writeJSONString(name)
379 self.writeJSONNumber(request_type)
380 self.writeJSONNumber(seqid)
381
382 def writeMessageEnd(self):
383 self.writeJSONArrayEnd()
384
385 def writeStructBegin(self, name):
386 self.writeJSONObjectStart()
387
388 def writeStructEnd(self):
389 self.writeJSONObjectEnd()
390
Roger Meierad8154a2012-12-18 21:02:16 +0100391 def writeFieldBegin(self, name, ttype, id):
Roger Meier85fb6de2012-11-02 00:05:42 +0000392 self.writeJSONNumber(id)
393 self.writeJSONObjectStart()
Roger Meierad8154a2012-12-18 21:02:16 +0100394 self.writeJSONString(CTYPES[ttype])
Roger Meier85fb6de2012-11-02 00:05:42 +0000395
396 def writeFieldEnd(self):
397 self.writeJSONObjectEnd()
398
399 def writeFieldStop(self):
400 pass
401
402 def writeMapBegin(self, ktype, vtype, size):
403 self.writeJSONArrayStart()
404 self.writeJSONString(CTYPES[ktype])
405 self.writeJSONString(CTYPES[vtype])
406 self.writeJSONNumber(size)
407 self.writeJSONObjectStart()
408
409 def writeMapEnd(self):
410 self.writeJSONObjectEnd()
411 self.writeJSONArrayEnd()
412
413 def writeListBegin(self, etype, size):
414 self.writeJSONArrayStart()
415 self.writeJSONString(CTYPES[etype])
416 self.writeJSONNumber(size)
417
418 def writeListEnd(self):
419 self.writeJSONArrayEnd()
420
421 def writeSetBegin(self, etype, size):
422 self.writeJSONArrayStart()
423 self.writeJSONString(CTYPES[etype])
424 self.writeJSONNumber(size)
425
426 def writeSetEnd(self):
427 self.writeJSONArrayEnd()
428
429 def writeBool(self, boolean):
430 self.writeJSONNumber(1 if boolean is True else 0)
431
432 def writeInteger(self, integer):
433 self.writeJSONNumber(integer)
434 writeByte = writeInteger
435 writeI16 = writeInteger
436 writeI32 = writeInteger
437 writeI64 = writeInteger
438
439 def writeDouble(self, dbl):
440 self.writeJSONNumber(dbl)
441
442 def writeString(self, string):
443 self.writeJSONString(string)
444
445 def writeBinary(self, binary):
446 self.writeJSONBase64(binary)
447
448class TJSONProtocolFactory:
449 def __init__(self):
450 pass
451
452 def getProtocol(self, trans):
453 return TJSONProtocol(trans)