blob: 1e51dd21d7890c88b39085586eed394fc03f5752 [file] [log] [blame]
David Reissea5e75a2008-03-07 20:12:20 +00001// Copyright (c) 2006- Facebook
2// Distributed under the Thrift Software License
3//
4// See accompanying file LICENSE or visit the Thrift site at:
5// http://developers.facebook.com/thrift/
6
7package com.facebook.thrift.protocol;
8
9import com.facebook.thrift.TException;
10import com.facebook.thrift.TByteArrayOutputStream;
11import com.facebook.thrift.transport.TTransport;
12import java.io.UnsupportedEncodingException;
13import java.util.Stack;
14
15/**
16 * JSON protocol implementation for thrift.
17 *
18 * This is a full-featured protocol supporting write and read.
19 *
20 * Please see the C++ class header for a detailed description of the
21 * protocol's wire format.
22 *
23 * @author Chad Walters <chad@powerset.com>
24 */
25public class TJSONProtocol extends TProtocol {
26
27 /**
28 * Factory for JSON protocol objects
29 */
30 public static class Factory implements TProtocolFactory {
31
32 public TProtocol getProtocol(TTransport trans) {
33 return new TJSONProtocol(trans);
34 }
35
36 }
37
38 private static final byte[] COMMA = new byte[] {','};
39 private static final byte[] COLON = new byte[] {':'};
40 private static final byte[] LBRACE = new byte[] {'{'};
41 private static final byte[] RBRACE = new byte[] {'}'};
42 private static final byte[] LBRACKET = new byte[] {'['};
43 private static final byte[] RBRACKET = new byte[] {']'};
44 private static final byte[] QUOTE = new byte[] {'"'};
45 private static final byte[] BACKSLASH = new byte[] {'\\'};
46 private static final byte[] ZERO = new byte[] {'0'};
47
48 private static final byte[] ESCSEQ = new byte[] {'\\','u','0','0'};
49
50 private static final long VERSION = 1;
51
52 private static final byte[] JSON_CHAR_TABLE = {
53 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
54 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
55 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
56 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
57 };
58
59 private static final String ESCAPE_CHARS = "\"\\bfnrt";
60
61 private static final byte[] ESCAPE_CHAR_VALS = {
62 '"', '\\', '\b', '\f', '\n', '\r', '\t',
63 };
64
65 private static final int DEF_STRING_SIZE = 16;
66
67 private static final byte[] NAME_BOOL = new byte[] {'t', 'f'};
68 private static final byte[] NAME_BYTE = new byte[] {'i','8'};
69 private static final byte[] NAME_I16 = new byte[] {'i','1','6'};
70 private static final byte[] NAME_I32 = new byte[] {'i','3','2'};
71 private static final byte[] NAME_I64 = new byte[] {'i','6','4'};
72 private static final byte[] NAME_DOUBLE = new byte[] {'d','b','l'};
73 private static final byte[] NAME_STRUCT = new byte[] {'r','e','c'};
74 private static final byte[] NAME_STRING = new byte[] {'s','t','r'};
75 private static final byte[] NAME_MAP = new byte[] {'m','a','p'};
76 private static final byte[] NAME_LIST = new byte[] {'l','s','t'};
77 private static final byte[] NAME_SET = new byte[] {'s','e','t'};
78
79 private static final byte[] getTypeNameForTypeID(byte typeID)
80 throws TException {
81 switch (typeID) {
82 case TType.BOOL:
83 return NAME_BOOL;
84 case TType.BYTE:
85 return NAME_BYTE;
86 case TType.I16:
87 return NAME_I16;
88 case TType.I32:
89 return NAME_I32;
90 case TType.I64:
91 return NAME_I64;
92 case TType.DOUBLE:
93 return NAME_DOUBLE;
94 case TType.STRING:
95 return NAME_STRING;
96 case TType.STRUCT:
97 return NAME_STRUCT;
98 case TType.MAP:
99 return NAME_MAP;
100 case TType.SET:
101 return NAME_SET;
102 case TType.LIST:
103 return NAME_LIST;
104 default:
105 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
106 "Unrecognized type");
107 }
108 }
109
110 private static final byte getTypeIDForTypeName(byte[] name)
111 throws TException {
112 byte result = TType.STOP;
113 if (name.length > 1) {
114 switch (name[0]) {
115 case 'd':
116 result = TType.DOUBLE;
117 break;
118 case 'i':
119 switch (name[1]) {
120 case '8':
121 result = TType.BYTE;
122 break;
123 case '1':
124 result = TType.I16;
125 break;
126 case '3':
127 result = TType.I32;
128 break;
129 case '6':
130 result = TType.I64;
131 break;
132 }
133 break;
134 case 'l':
135 result = TType.LIST;
136 break;
137 case 'm':
138 result = TType.MAP;
139 break;
140 case 'r':
141 result = TType.STRUCT;
142 break;
143 case 's':
144 if (name[1] == 't') {
145 result = TType.STRING;
146 }
147 else if (name[1] == 'e') {
148 result = TType.SET;
149 }
150 break;
151 case 't':
152 result = TType.BOOL;
153 break;
154 }
155 }
156 if (result == TType.STOP) {
157 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
158 "Unrecognized type");
159 }
160 return result;
161 }
162
163 // Base class for tracking JSON contexts that may require inserting/reading
164 // additional JSON syntax characters
165 // This base context does nothing.
166 protected class JSONBaseContext {
167 protected void write() throws TException {}
168
169 protected void read() throws TException {}
170
171 protected boolean escapeNum() { return false; }
172 }
173
174 // Context for JSON lists. Will insert/read commas before each item except
175 // for the first one
176 protected class JSONListContext extends JSONBaseContext {
177 private boolean first_ = true;
178
179 @Override
180 protected void write() throws TException {
181 if (first_) {
182 first_ = false;
183 } else {
184 trans_.write(COMMA);
185 }
186 }
187
188 @Override
189 protected void read() throws TException {
190 if (first_) {
191 first_ = false;
192 } else {
193 readJSONSyntaxChar(COMMA);
194 }
195 }
196 }
197
198 // Context for JSON records. Will insert/read colons before the value portion
199 // of each record pair, and commas before each key except the first. In
200 // addition, will indicate that numbers in the key position need to be
201 // escaped in quotes (since JSON keys must be strings).
202 protected class JSONPairContext extends JSONBaseContext {
203 private boolean first_ = true;
204 private boolean colon_ = true;
205
206 @Override
207 protected void write() throws TException {
208 if (first_) {
209 first_ = false;
210 colon_ = true;
211 } else {
212 trans_.write(colon_ ? COLON : COMMA);
213 colon_ = !colon_;
214 }
215 }
216
217 @Override
218 protected void read() throws TException {
219 if (first_) {
220 first_ = false;
221 colon_ = true;
222 } else {
223 readJSONSyntaxChar(colon_ ? COLON : COMMA);
224 colon_ = !colon_;
225 }
226 }
227
228 @Override
229 protected boolean escapeNum() {
230 return colon_;
231 }
232 }
233
234 // Holds up to one byte from the transport
235 protected class LookaheadReader {
236
237 private boolean hasData_;
238 private byte[] data_ = new byte[1];
239
240 // Return and consume the next byte to be read, either taking it from the
241 // data buffer if present or getting it from the transport otherwise.
242 protected byte read() throws TException {
243 if (hasData_) {
244 hasData_ = false;
245 }
246 else {
247 trans_.readAll(data_, 0, 1);
248 }
249 return data_[0];
250 }
251
252 // Return the next byte to be read without consuming, filling the data
253 // buffer if it has not been filled already.
254 protected byte peek() throws TException {
255 if (!hasData_) {
256 trans_.readAll(data_, 0, 1);
257 }
258 hasData_ = true;
259 return data_[0];
260 }
261 }
262
263 // Stack of nested contexts that we may be in
264 private Stack<JSONBaseContext> contextStack_ = new Stack<JSONBaseContext>();
265
266 // Current context that we are in
267 private JSONBaseContext context_ = new JSONBaseContext();
268
269 // Reader that manages a 1-byte buffer
270 private LookaheadReader reader_ = new LookaheadReader();
271
272 // Push a new JSON context onto the stack.
273 private void pushContext(JSONBaseContext c) {
274 contextStack_.push(context_);
275 context_ = c;
276 }
277
278 // Pop the last JSON context off the stack
279 private void popContext() {
280 context_ = contextStack_.pop();
281 }
282
283 /**
284 * Constructor
285 */
286 public TJSONProtocol(TTransport trans) {
287 super(trans);
288 }
289
290 // Temporary buffer used by several methods
291 private byte[] tmpbuf_ = new byte[4];
292
293 // Read a byte that must match b[0]; otherwise an excpetion is thrown.
294 // Marked protected to avoid synthetic accessor in JSONListContext.read
295 // and JSONPairContext.read
296 protected void readJSONSyntaxChar(byte[] b) throws TException {
297 byte ch = reader_.read();
298 if (ch != b[0]) {
299 throw new TProtocolException(TProtocolException.INVALID_DATA,
300 "Unexpected character:" + (char)ch);
301 }
302 }
303
304 // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
305 // corresponding hex value
306 private static final byte hexVal(byte ch) throws TException {
307 if ((ch >= '0') && (ch <= '9')) {
308 return (byte)((char)ch - '0');
309 }
310 else if ((ch >= 'a') && (ch <= 'f')) {
311 return (byte)((char)ch - 'a');
312 }
313 else {
314 throw new TProtocolException(TProtocolException.INVALID_DATA,
315 "Expected hex character");
316 }
317 }
318
319 // Convert a byte containing a hex value to its corresponding hex character
320 private static final byte hexChar(byte val) {
321 val &= 0x0F;
322 if (val < 10) {
323 return (byte)((char)val + '0');
324 }
325 else {
326 return (byte)((char)val + 'a');
327 }
328 }
329
330 // Write the bytes in array buf as a JSON characters, escaping as needed
331 private void writeJSONString(byte[] b) throws TException {
332 context_.write();
333 trans_.write(QUOTE);
334 int len = b.length;
335 for (int i = 0; i < len; i++) {
336 if ((b[i] & 0x00FF) >= 0x30) {
337 if (b[i] == BACKSLASH[0]) {
338 trans_.write(BACKSLASH);
339 trans_.write(BACKSLASH);
340 }
341 else {
342 trans_.write(b, i, 1);
343 }
344 }
345 else {
346 tmpbuf_[0] = JSON_CHAR_TABLE[b[i]];
347 if (tmpbuf_[0] == 1) {
348 trans_.write(b, i, 1);
349 }
350 else if (tmpbuf_[0] > 1) {
351 trans_.write(BACKSLASH);
352 trans_.write(tmpbuf_, 0, 1);
353 }
354 else {
355 trans_.write(ESCSEQ);
356 tmpbuf_[0] = hexChar((byte)(b[i] >> 4));
357 tmpbuf_[1] = hexChar(b[i]);
358 trans_.write(tmpbuf_, 0, 2);
359 }
360 }
361 }
362 trans_.write(QUOTE);
363 }
364
365 // Write out number as a JSON value. If the context dictates so, it will be
366 // wrapped in quotes to output as a JSON string.
367 private void writeJSONInteger(long num) throws TException {
368 context_.write();
369 String str = Long.toString(num);
370 boolean escapeNum = context_.escapeNum();
371 if (escapeNum) {
372 trans_.write(QUOTE);
373 }
374 try {
375 byte[] buf = str.getBytes("UTF-8");
376 trans_.write(buf);
377 } catch (UnsupportedEncodingException uex) {
378 throw new TException("JVM DOES NOT SUPPORT UTF-8");
379 }
380 if (escapeNum) {
381 trans_.write(QUOTE);
382 }
383 }
384
385 // Write out a double as a JSON value. If it is NaN or infinity or if the
386 // context dictates escaping, write out as JSON string.
387 private void writeJSONDouble(double num) throws TException {
388 context_.write();
389 String str = Double.toString(num);
390 boolean special = false;
391 switch (str.charAt(0)) {
392 case 'N': // NaN
393 case 'I': // Infinity
394 special = true;
395 break;
396 case '-':
397 if (str.charAt(1) == 'I') { // -Infinity
398 special = true;
399 }
400 break;
401 }
402
403 boolean escapeNum = special || context_.escapeNum();
404 if (escapeNum) {
405 trans_.write(QUOTE);
406 }
407 try {
408 byte[] b = str.getBytes("UTF-8");
409 trans_.write(b, 0, b.length);
410 } catch (UnsupportedEncodingException uex) {
411 throw new TException("JVM DOES NOT SUPPORT UTF-8");
412 }
413 if (escapeNum) {
414 trans_.write(QUOTE);
415 }
416 }
417
418 // Write out contents of byte array b as a JSON string with base-64 encoded
419 // data
420 private void writeJSONBase64(byte[] b) throws TException {
421 context_.write();
422 trans_.write(QUOTE);
423 int len = b.length;
424 int off = 0;
425 while (len >= 3) {
426 // Encode 3 bytes at a time
427 TBase64Utils.encode(b, off, 3, tmpbuf_, 0);
428 trans_.write(tmpbuf_, 0, 4);
429 off += 3;
430 len -= 3;
431 }
432 if (len > 0) {
433 // Encode remainder
434 TBase64Utils.encode(b, off, len, tmpbuf_, 0);
435 trans_.write(tmpbuf_, 0, len + 1);
436 }
437 trans_.write(QUOTE);
438 }
439
440 private void writeJSONObjectStart() throws TException {
441 context_.write();
442 trans_.write(LBRACE);
443 pushContext(new JSONPairContext());
444 }
445
446 private void writeJSONObjectEnd() throws TException {
447 popContext();
448 trans_.write(RBRACE);
449 }
450
451 private void writeJSONArrayStart() throws TException {
452 context_.write();
453 trans_.write(LBRACKET);
454 pushContext(new JSONListContext());
455 }
456
457 private void writeJSONArrayEnd() throws TException {
458 popContext();
459 trans_.write(RBRACKET);
460 }
461
462 @Override
463 public void writeMessageBegin(TMessage message) throws TException {
464 writeJSONArrayStart();
465 writeJSONInteger(VERSION);
466 try {
467 byte[] b = message.name.getBytes("UTF-8");
468 writeJSONString(b);
469 } catch (UnsupportedEncodingException uex) {
470 throw new TException("JVM DOES NOT SUPPORT UTF-8");
471 }
472 writeJSONInteger(message.type);
473 writeJSONInteger(message.seqid);
474 }
475
476 @Override
477 public void writeMessageEnd() throws TException {
478 writeJSONArrayEnd();
479 }
480
481 @Override
482 public void writeStructBegin(TStruct struct) throws TException {
483 writeJSONObjectStart();
484 }
485
486 @Override
487 public void writeStructEnd() throws TException {
488 writeJSONObjectEnd();
489 }
490
491 @Override
492 public void writeFieldBegin(TField field) throws TException {
493 writeJSONInteger(field.id);
494 writeJSONObjectStart();
495 writeJSONString(getTypeNameForTypeID(field.type));
496 }
497
498 @Override
499 public void writeFieldEnd() throws TException {
500 writeJSONObjectEnd();
501 }
502
503 @Override
504 public void writeFieldStop() {}
505
506 @Override
507 public void writeMapBegin(TMap map) throws TException {
508 writeJSONArrayStart();
509 writeJSONString(getTypeNameForTypeID(map.keyType));
510 writeJSONString(getTypeNameForTypeID(map.valueType));
511 writeJSONInteger(map.size);
512 writeJSONObjectStart();
513 }
514
515 @Override
516 public void writeMapEnd() throws TException {
517 writeJSONObjectEnd();
518 writeJSONArrayEnd();
519 }
520
521 @Override
522 public void writeListBegin(TList list) throws TException {
523 writeJSONArrayStart();
524 writeJSONString(getTypeNameForTypeID(list.elemType));
525 writeJSONInteger(list.size);
526 }
527
528 @Override
529 public void writeListEnd() throws TException {
530 writeJSONArrayEnd();
531 }
532
533 @Override
534 public void writeSetBegin(TSet set) throws TException {
535 writeJSONArrayStart();
536 writeJSONString(getTypeNameForTypeID(set.elemType));
537 writeJSONInteger(set.size);
538 }
539
540 @Override
541 public void writeSetEnd() throws TException {
542 writeJSONArrayEnd();
543 }
544
545 @Override
546 public void writeBool(boolean b) throws TException {
547 writeJSONInteger(b ? (long)1 : (long)0);
548 }
549
550 @Override
551 public void writeByte(byte b) throws TException {
552 writeJSONInteger((long)b);
553 }
554
555 @Override
556 public void writeI16(short i16) throws TException {
557 writeJSONInteger((long)i16);
558 }
559
560 @Override
561 public void writeI32(int i32) throws TException {
562 writeJSONInteger((long)i32);
563 }
564
565 @Override
566 public void writeI64(long i64) throws TException {
567 writeJSONInteger(i64);
568 }
569
570 @Override
571 public void writeDouble(double dub) throws TException {
572 writeJSONDouble(dub);
573 }
574
575 @Override
576 public void writeString(String str) throws TException {
577 try {
578 byte[] b = str.getBytes("UTF-8");
579 writeJSONString(b);
580 } catch (UnsupportedEncodingException uex) {
581 throw new TException("JVM DOES NOT SUPPORT UTF-8");
582 }
583 }
584
585 @Override
586 public void writeBinary(byte[] bin) throws TException {
587 writeJSONBase64(bin);
588 }
589
590 /**
591 * Reading methods.
592 */
593
594 // Read in a JSON string, unescaping as appropriate.. Skip reading from the
595 // context if skipContext is true.
596 private TByteArrayOutputStream readJSONString(boolean skipContext)
597 throws TException {
598 TByteArrayOutputStream arr = new TByteArrayOutputStream(DEF_STRING_SIZE);
599 if (!skipContext) {
600 context_.read();
601 }
602 readJSONSyntaxChar(QUOTE);
603 while (true) {
604 byte ch = reader_.read();
605 if (ch == QUOTE[0]) {
606 break;
607 }
608 if (ch == ESCSEQ[0]) {
609 ch = reader_.read();
610 if (ch == ESCSEQ[1]) {
611 readJSONSyntaxChar(ZERO);
612 readJSONSyntaxChar(ZERO);
613 trans_.readAll(tmpbuf_, 0, 2);
614 ch = (byte)((hexVal((byte)tmpbuf_[0]) << 4) + hexVal(tmpbuf_[1]));
615 }
616 else {
617 int off = ESCAPE_CHARS.indexOf(ch);
618 if (off == -1) {
619 throw new TProtocolException(TProtocolException.INVALID_DATA,
620 "Expected control char");
621 }
622 ch = ESCAPE_CHAR_VALS[off];
623 }
624 }
625 arr.write(ch);
626 }
627 return arr;
628 }
629
630 // Return true if the given byte could be a valid part of a JSON number.
631 private boolean isJSONNumeric(byte b) {
632 switch (b) {
633 case '+':
634 case '-':
635 case '.':
636 case '0':
637 case '1':
638 case '2':
639 case '3':
640 case '4':
641 case '5':
642 case '6':
643 case '7':
644 case '8':
645 case '9':
646 case 'E':
647 case 'e':
648 return true;
649 }
650 return false;
651 }
652
653 // Read in a sequence of characters that are all valid in JSON numbers. Does
654 // not do a complete regex check to validate that this is actually a number.
655 private String readJSONNumericChars() throws TException {
656 StringBuilder strbld = new StringBuilder();
657 while (true) {
658 byte ch = reader_.peek();
659 if (!isJSONNumeric(ch)) {
660 break;
661 }
662 strbld.append((char)reader_.read());
663 }
664 return strbld.toString();
665 }
666
667 // Read in a JSON number. If the context dictates, read in enclosing quotes.
668 private long readJSONInteger() throws TException {
669 context_.read();
670 if (context_.escapeNum()) {
671 readJSONSyntaxChar(QUOTE);
672 }
673 String str = readJSONNumericChars();
674 if (context_.escapeNum()) {
675 readJSONSyntaxChar(QUOTE);
676 }
677 try {
678 return Long.valueOf(str);
679 }
680 catch (NumberFormatException ex) {
681 throw new TProtocolException(TProtocolException.INVALID_DATA,
682 "Bad data encounted in numeric data");
683 }
684 }
685
686 // Read in a JSON double value. Throw if the value is not wrapped in quotes
687 // when expected or if wrapped in quotes when not expected.
688 private double readJSONDouble() throws TException {
689 context_.read();
690 if (reader_.peek() == QUOTE[0]) {
691 TByteArrayOutputStream arr = readJSONString(true);
692 try {
693 double dub = Double.valueOf(arr.toString("UTF-8"));
694 if (!context_.escapeNum() && !Double.isNaN(dub) &&
695 !Double.isInfinite(dub)) {
696 // Throw exception -- we should not be in a string in this case
697 throw new TProtocolException(TProtocolException.INVALID_DATA,
698 "Numeric data unexpectedly quoted");
699 }
700 return dub;
701 }
702 catch (UnsupportedEncodingException ex) {
703 throw new TException("JVM DOES NOT SUPPORT UTF-8");
704 }
705 }
706 else {
707 if (context_.escapeNum()) {
708 // This will throw - we should have had a quote if escapeNum == true
709 readJSONSyntaxChar(QUOTE);
710 }
711 try {
712 return Double.valueOf(readJSONNumericChars());
713 }
714 catch (NumberFormatException ex) {
715 throw new TProtocolException(TProtocolException.INVALID_DATA,
716 "Bad data encounted in numeric data");
717 }
718 }
719 }
720
721 // Read in a JSON string containing base-64 encoded data and decode it.
722 private byte[] readJSONBase64() throws TException {
723 TByteArrayOutputStream arr = readJSONString(false);
724 byte[] b = arr.get();
725 int len = arr.len();
726 int off = 0;
727 int size = 0;
728 while (len >= 4) {
729 // Decode 4 bytes at a time
730 TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
731 off += 4;
732 len -= 4;
733 size += 3;
734 }
735 // Don't decode if we hit the end or got a single leftover byte (invalid
736 // base64 but legal for skip of regular string type)
737 if (len > 1) {
738 // Decode remainder
739 TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
740 size += len - 1;
741 }
742 // Sadly we must copy the byte[] (any way around this?)
743 byte [] result = new byte[size];
744 System.arraycopy(b, 0, result, 0, size);
745 return result;
746 }
747
748 private void readJSONObjectStart() throws TException {
749 context_.read();
750 readJSONSyntaxChar(LBRACE);
751 pushContext(new JSONPairContext());
752 }
753
754 private void readJSONObjectEnd() throws TException {
755 readJSONSyntaxChar(RBRACE);
756 popContext();
757 }
758
759 private void readJSONArrayStart() throws TException {
760 context_.read();
761 readJSONSyntaxChar(LBRACKET);
762 pushContext(new JSONListContext());
763 }
764
765 private void readJSONArrayEnd() throws TException {
766 readJSONSyntaxChar(RBRACKET);
767 popContext();
768 }
769
770 @Override
771 public TMessage readMessageBegin() throws TException {
772 TMessage message = new TMessage();
773 readJSONArrayStart();
774 if (readJSONInteger() != VERSION) {
775 throw new TProtocolException(TProtocolException.BAD_VERSION,
776 "Message contained bad version.");
777 }
778 try {
779 message.name = readJSONString(false).toString("UTF-8");
780 }
781 catch (UnsupportedEncodingException ex) {
782 throw new TException("JVM DOES NOT SUPPORT UTF-8");
783 }
784 message.type = (byte) readJSONInteger();
785 message.seqid = (int) readJSONInteger();
786 return message;
787 }
788
789 @Override
790 public void readMessageEnd() throws TException {
791 readJSONArrayEnd();
792 }
793
794 @Override
795 public TStruct readStructBegin() throws TException {
796 readJSONObjectStart();
797 return new TStruct();
798 }
799
800 @Override
801 public void readStructEnd() throws TException {
802 readJSONObjectEnd();
803 }
804
805 @Override
806 public TField readFieldBegin() throws TException {
807 TField field = new TField();
808 byte ch = reader_.peek();
809 if (ch == RBRACE[0]) {
810 field.type = TType.STOP;
811 }
812 else {
813 field.id = (short) readJSONInteger();
814 readJSONObjectStart();
815 field.type = getTypeIDForTypeName(readJSONString(false).get());
816 }
817 return field;
818 }
819
820 @Override
821 public void readFieldEnd() throws TException {
822 readJSONObjectEnd();
823 }
824
825 @Override
826 public TMap readMapBegin() throws TException {
827 TMap map = new TMap();
828 readJSONArrayStart();
829 map.keyType = getTypeIDForTypeName(readJSONString(false).get());
830 map.valueType = getTypeIDForTypeName(readJSONString(false).get());
831 map.size = (int)readJSONInteger();
832 readJSONObjectStart();
833 return map;
834 }
835
836 @Override
837 public void readMapEnd() throws TException {
838 readJSONObjectEnd();
839 readJSONArrayEnd();
840 }
841
842 @Override
843 public TList readListBegin() throws TException {
844 TList list = new TList();
845 readJSONArrayStart();
846 list.elemType = getTypeIDForTypeName(readJSONString(false).get());
847 list.size = (int)readJSONInteger();
848 return list;
849 }
850
851 @Override
852 public void readListEnd() throws TException {
853 readJSONArrayEnd();
854 }
855
856 @Override
857 public TSet readSetBegin() throws TException {
858 TSet set = new TSet();
859 readJSONArrayStart();
860 set.elemType = getTypeIDForTypeName(readJSONString(false).get());
861 set.size = (int)readJSONInteger();
862 return set;
863 }
864
865 @Override
866 public void readSetEnd() throws TException {
867 readJSONArrayEnd();
868 }
869
870 @Override
871 public boolean readBool() throws TException {
872 return (readJSONInteger() == 0 ? false : true);
873 }
874
875 @Override
876 public byte readByte() throws TException {
877 return (byte) readJSONInteger();
878 }
879
880 @Override
881 public short readI16() throws TException {
882 return (short) readJSONInteger();
883 }
884
885 @Override
886 public int readI32() throws TException {
887 return (int) readJSONInteger();
888 }
889
890 @Override
891 public long readI64() throws TException {
892 return (long) readJSONInteger();
893 }
894
895 @Override
896 public double readDouble() throws TException {
897 return readJSONDouble();
898 }
899
900 @Override
901 public String readString() throws TException {
902 try {
903 return readJSONString(false).toString("UTF-8");
904 }
905 catch (UnsupportedEncodingException ex) {
906 throw new TException("JVM DOES NOT SUPPORT UTF-8");
907 }
908 }
909
910 @Override
911 public byte[] readBinary() throws TException {
912 return readJSONBase64();
913 }
914
915}