blob: 9dbdea9aae033fe27e3b60066bd5269c7c55e0e6 [file] [log] [blame]
Bryan Duxburyfd32d792010-09-18 20:51:25 +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
20using System;
21using System.IO;
22using System.Text;
23using System.Collections.Generic;
24
25using Thrift.Transport;
Roger Meier3da317b2011-07-28 18:35:51 +000026using System.Globalization;
Bryan Duxburyfd32d792010-09-18 20:51:25 +000027
28namespace Thrift.Protocol
29{
Jens Geyerd5436f52014-10-03 19:50:38 +020030 /// <summary>
31 /// JSON protocol implementation for thrift.
Christian Weiss8fb719e2018-03-30 21:26:04 +020032 /// <para/>
Jens Geyerd5436f52014-10-03 19:50:38 +020033 /// This is a full-featured protocol supporting Write and Read.
Christian Weiss8fb719e2018-03-30 21:26:04 +020034 /// <para/>
Jens Geyerd5436f52014-10-03 19:50:38 +020035 /// Please see the C++ class header for a detailed description of the
36 /// protocol's wire format.
Christian Weiss8fb719e2018-03-30 21:26:04 +020037 /// <para/>
Jens Geyerd5436f52014-10-03 19:50:38 +020038 /// Adapted from the Java version.
39 /// </summary>
40 public class TJSONProtocol : TProtocol
41 {
42 /// <summary>
Christian Weiss8fb719e2018-03-30 21:26:04 +020043 /// Factory for JSON protocol objects.
Jens Geyerd5436f52014-10-03 19:50:38 +020044 /// </summary>
45 public class Factory : TProtocolFactory
46 {
47 public TProtocol GetProtocol(TTransport trans)
48 {
49 return new TJSONProtocol(trans);
50 }
51 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +000052
Jens Geyerd5436f52014-10-03 19:50:38 +020053 private static byte[] COMMA = new byte[] { (byte)',' };
54 private static byte[] COLON = new byte[] { (byte)':' };
55 private static byte[] LBRACE = new byte[] { (byte)'{' };
56 private static byte[] RBRACE = new byte[] { (byte)'}' };
57 private static byte[] LBRACKET = new byte[] { (byte)'[' };
58 private static byte[] RBRACKET = new byte[] { (byte)']' };
59 private static byte[] QUOTE = new byte[] { (byte)'"' };
60 private static byte[] BACKSLASH = new byte[] { (byte)'\\' };
Bryan Duxburyfd32d792010-09-18 20:51:25 +000061
Jens Geyerd5436f52014-10-03 19:50:38 +020062 private byte[] ESCSEQ = new byte[] { (byte)'\\', (byte)'u', (byte)'0', (byte)'0' };
Bryan Duxburyfd32d792010-09-18 20:51:25 +000063
Jens Geyerd5436f52014-10-03 19:50:38 +020064 private const long VERSION = 1;
65 private byte[] JSON_CHAR_TABLE = {
66 0, 0, 0, 0, 0, 0, 0, 0,(byte)'b',(byte)'t',(byte)'n', 0,(byte)'f',(byte)'r', 0, 0,
67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68 1, 1,(byte)'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
Bryan Duxburyfd32d792010-09-18 20:51:25 +000069 };
70
Jens Geyerd5436f52014-10-03 19:50:38 +020071 private char[] ESCAPE_CHARS = "\"\\/bfnrt".ToCharArray();
Bryan Duxburyfd32d792010-09-18 20:51:25 +000072
Jens Geyerd5436f52014-10-03 19:50:38 +020073 private byte[] ESCAPE_CHAR_VALS = {
74 (byte)'"', (byte)'\\', (byte)'/', (byte)'\b', (byte)'\f', (byte)'\n', (byte)'\r', (byte)'\t',
Bryan Duxburyfd32d792010-09-18 20:51:25 +000075 };
76
Jens Geyerd5436f52014-10-03 19:50:38 +020077 private const int DEF_STRING_SIZE = 16;
Bryan Duxburyfd32d792010-09-18 20:51:25 +000078
Jens Geyerd5436f52014-10-03 19:50:38 +020079 private static byte[] NAME_BOOL = new byte[] { (byte)'t', (byte)'f' };
80 private static byte[] NAME_BYTE = new byte[] { (byte)'i', (byte)'8' };
81 private static byte[] NAME_I16 = new byte[] { (byte)'i', (byte)'1', (byte)'6' };
82 private static byte[] NAME_I32 = new byte[] { (byte)'i', (byte)'3', (byte)'2' };
83 private static byte[] NAME_I64 = new byte[] { (byte)'i', (byte)'6', (byte)'4' };
84 private static byte[] NAME_DOUBLE = new byte[] { (byte)'d', (byte)'b', (byte)'l' };
85 private static byte[] NAME_STRUCT = new byte[] { (byte)'r', (byte)'e', (byte)'c' };
86 private static byte[] NAME_STRING = new byte[] { (byte)'s', (byte)'t', (byte)'r' };
87 private static byte[] NAME_MAP = new byte[] { (byte)'m', (byte)'a', (byte)'p' };
88 private static byte[] NAME_LIST = new byte[] { (byte)'l', (byte)'s', (byte)'t' };
89 private static byte[] NAME_SET = new byte[] { (byte)'s', (byte)'e', (byte)'t' };
Bryan Duxburyfd32d792010-09-18 20:51:25 +000090
Jens Geyerd5436f52014-10-03 19:50:38 +020091 private static byte[] GetTypeNameForTypeID(TType typeID)
92 {
93 switch (typeID)
94 {
95 case TType.Bool:
96 return NAME_BOOL;
97 case TType.Byte:
98 return NAME_BYTE;
99 case TType.I16:
100 return NAME_I16;
101 case TType.I32:
102 return NAME_I32;
103 case TType.I64:
104 return NAME_I64;
105 case TType.Double:
106 return NAME_DOUBLE;
107 case TType.String:
108 return NAME_STRING;
109 case TType.Struct:
110 return NAME_STRUCT;
111 case TType.Map:
112 return NAME_MAP;
113 case TType.Set:
114 return NAME_SET;
115 case TType.List:
116 return NAME_LIST;
117 default:
118 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
119 "Unrecognized type");
120 }
121 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000122
Jens Geyerd5436f52014-10-03 19:50:38 +0200123 private static TType GetTypeIDForTypeName(byte[] name)
124 {
125 TType result = TType.Stop;
126 if (name.Length > 1)
127 {
128 switch (name[0])
129 {
130 case (byte)'d':
131 result = TType.Double;
132 break;
133 case (byte)'i':
134 switch (name[1])
135 {
136 case (byte)'8':
137 result = TType.Byte;
138 break;
139 case (byte)'1':
140 result = TType.I16;
141 break;
142 case (byte)'3':
143 result = TType.I32;
144 break;
145 case (byte)'6':
146 result = TType.I64;
147 break;
148 }
149 break;
150 case (byte)'l':
151 result = TType.List;
152 break;
153 case (byte)'m':
154 result = TType.Map;
155 break;
156 case (byte)'r':
157 result = TType.Struct;
158 break;
159 case (byte)'s':
160 if (name[1] == (byte)'t')
161 {
162 result = TType.String;
163 }
164 else if (name[1] == (byte)'e')
165 {
166 result = TType.Set;
167 }
168 break;
169 case (byte)'t':
170 result = TType.Bool;
171 break;
172 }
173 }
174 if (result == TType.Stop)
175 {
176 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
177 "Unrecognized type");
178 }
179 return result;
180 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000181
Christian Weiss8fb719e2018-03-30 21:26:04 +0200182 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200183 /// Base class for tracking JSON contexts that may require
184 /// inserting/Reading additional JSON syntax characters
185 /// This base context does nothing.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200186 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200187 protected class JSONBaseContext
188 {
189 protected TJSONProtocol proto;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000190
Jens Geyerd5436f52014-10-03 19:50:38 +0200191 public JSONBaseContext(TJSONProtocol proto)
192 {
193 this.proto = proto;
194 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000195
Jens Geyerd5436f52014-10-03 19:50:38 +0200196 public virtual void Write() { }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000197
Jens Geyerd5436f52014-10-03 19:50:38 +0200198 public virtual void Read() { }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000199
Jens Geyerd5436f52014-10-03 19:50:38 +0200200 public virtual bool EscapeNumbers() { return false; }
201 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000202
Christian Weiss8fb719e2018-03-30 21:26:04 +0200203 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200204 /// Context for JSON lists. Will insert/Read commas before each item except
205 /// for the first one
Christian Weiss8fb719e2018-03-30 21:26:04 +0200206 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200207 protected class JSONListContext : JSONBaseContext
208 {
209 public JSONListContext(TJSONProtocol protocol)
210 : base(protocol)
211 {
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000212
Jens Geyerd5436f52014-10-03 19:50:38 +0200213 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000214
Jens Geyerd5436f52014-10-03 19:50:38 +0200215 private bool first = true;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000216
Jens Geyerd5436f52014-10-03 19:50:38 +0200217 public override void Write()
218 {
219 if (first)
220 {
221 first = false;
222 }
223 else
224 {
225 proto.trans.Write(COMMA);
226 }
227 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000228
Jens Geyerd5436f52014-10-03 19:50:38 +0200229 public override void Read()
230 {
231 if (first)
232 {
233 first = false;
234 }
235 else
236 {
237 proto.ReadJSONSyntaxChar(COMMA);
238 }
239 }
240 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000241
Christian Weiss8fb719e2018-03-30 21:26:04 +0200242 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200243 /// Context for JSON records. Will insert/Read colons before the value portion
244 /// of each record pair, and commas before each key except the first. In
245 /// addition, will indicate that numbers in the key position need to be
246 /// escaped in quotes (since JSON keys must be strings).
Christian Weiss8fb719e2018-03-30 21:26:04 +0200247 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200248 protected class JSONPairContext : JSONBaseContext
249 {
250 public JSONPairContext(TJSONProtocol proto)
251 : base(proto)
252 {
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000253
Jens Geyerd5436f52014-10-03 19:50:38 +0200254 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000255
Jens Geyerd5436f52014-10-03 19:50:38 +0200256 private bool first = true;
257 private bool colon = true;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000258
Jens Geyerd5436f52014-10-03 19:50:38 +0200259 public override void Write()
260 {
261 if (first)
262 {
263 first = false;
264 colon = true;
265 }
266 else
267 {
268 proto.trans.Write(colon ? COLON : COMMA);
269 colon = !colon;
270 }
271 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000272
Jens Geyerd5436f52014-10-03 19:50:38 +0200273 public override void Read()
274 {
275 if (first)
276 {
277 first = false;
278 colon = true;
279 }
280 else
281 {
282 proto.ReadJSONSyntaxChar(colon ? COLON : COMMA);
283 colon = !colon;
284 }
285 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000286
Jens Geyerd5436f52014-10-03 19:50:38 +0200287 public override bool EscapeNumbers()
288 {
289 return colon;
290 }
291 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000292
Christian Weiss8fb719e2018-03-30 21:26:04 +0200293 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200294 /// Holds up to one byte from the transport
Christian Weiss8fb719e2018-03-30 21:26:04 +0200295 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200296 protected class LookaheadReader
297 {
298 protected TJSONProtocol proto;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000299
Jens Geyerd5436f52014-10-03 19:50:38 +0200300 public LookaheadReader(TJSONProtocol proto)
301 {
302 this.proto = proto;
303 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000304
Jens Geyerd5436f52014-10-03 19:50:38 +0200305 private bool hasData;
306 private byte[] data = new byte[1];
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000307
Christian Weiss8fb719e2018-03-30 21:26:04 +0200308 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200309 /// Return and consume the next byte to be Read, either taking it from the
310 /// data buffer if present or getting it from the transport otherwise.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200311 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200312 public byte Read()
313 {
314 if (hasData)
315 {
316 hasData = false;
317 }
318 else
319 {
320 proto.trans.ReadAll(data, 0, 1);
321 }
322 return data[0];
323 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000324
Christian Weiss8fb719e2018-03-30 21:26:04 +0200325 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200326 /// Return the next byte to be Read without consuming, filling the data
327 /// buffer if it has not been filled alReady.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200328 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200329 public byte Peek()
330 {
331 if (!hasData)
332 {
333 proto.trans.ReadAll(data, 0, 1);
334 }
335 hasData = true;
336 return data[0];
337 }
338 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000339
Jens Geyerd5436f52014-10-03 19:50:38 +0200340 // Default encoding
341 protected Encoding utf8Encoding = UTF8Encoding.UTF8;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000342
Jens Geyerd5436f52014-10-03 19:50:38 +0200343 // Stack of nested contexts that we may be in
344 protected Stack<JSONBaseContext> contextStack = new Stack<JSONBaseContext>();
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000345
Jens Geyerd5436f52014-10-03 19:50:38 +0200346 // Current context that we are in
347 protected JSONBaseContext context;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000348
Jens Geyerd5436f52014-10-03 19:50:38 +0200349 // Reader that manages a 1-byte buffer
350 protected LookaheadReader reader;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000351
Christian Weiss8fb719e2018-03-30 21:26:04 +0200352 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200353 /// Push a new JSON context onto the stack.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200354 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200355 protected void PushContext(JSONBaseContext c)
356 {
357 contextStack.Push(context);
358 context = c;
359 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000360
Christian Weiss8fb719e2018-03-30 21:26:04 +0200361 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200362 /// Pop the last JSON context off the stack
Christian Weiss8fb719e2018-03-30 21:26:04 +0200363 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200364 protected void PopContext()
365 {
366 context = contextStack.Pop();
367 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000368
Christian Weiss8fb719e2018-03-30 21:26:04 +0200369 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200370 /// TJSONProtocol Constructor
Christian Weiss8fb719e2018-03-30 21:26:04 +0200371 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200372 public TJSONProtocol(TTransport trans)
373 : base(trans)
374 {
375 context = new JSONBaseContext(this);
376 reader = new LookaheadReader(this);
377 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000378
Jens Geyerd5436f52014-10-03 19:50:38 +0200379 // Temporary buffer used by several methods
380 private byte[] tempBuffer = new byte[4];
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000381
Christian Weiss8fb719e2018-03-30 21:26:04 +0200382 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200383 /// Read a byte that must match b[0]; otherwise an exception is thrown.
384 /// Marked protected to avoid synthetic accessor in JSONListContext.Read
385 /// and JSONPairContext.Read
Christian Weiss8fb719e2018-03-30 21:26:04 +0200386 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200387 protected void ReadJSONSyntaxChar(byte[] b)
388 {
389 byte ch = reader.Read();
390 if (ch != b[0])
391 {
392 throw new TProtocolException(TProtocolException.INVALID_DATA,
393 "Unexpected character:" + (char)ch);
394 }
395 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000396
Christian Weiss8fb719e2018-03-30 21:26:04 +0200397 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200398 /// Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
399 /// corresponding hex value
Christian Weiss8fb719e2018-03-30 21:26:04 +0200400 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200401 private static byte HexVal(byte ch)
402 {
403 if ((ch >= '0') && (ch <= '9'))
404 {
405 return (byte)((char)ch - '0');
406 }
407 else if ((ch >= 'a') && (ch <= 'f'))
408 {
409 ch += 10;
410 return (byte)((char)ch - 'a');
411 }
412 else
413 {
414 throw new TProtocolException(TProtocolException.INVALID_DATA,
415 "Expected hex character");
416 }
417 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000418
Christian Weiss8fb719e2018-03-30 21:26:04 +0200419 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200420 /// Convert a byte containing a hex value to its corresponding hex character
Christian Weiss8fb719e2018-03-30 21:26:04 +0200421 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200422 private static byte HexChar(byte val)
423 {
424 val &= 0x0F;
425 if (val < 10)
426 {
427 return (byte)((char)val + '0');
428 }
429 else
430 {
431 val -= 10;
432 return (byte)((char)val + 'a');
433 }
434 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000435
Christian Weiss8fb719e2018-03-30 21:26:04 +0200436 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200437 /// Write the bytes in array buf as a JSON characters, escaping as needed
Christian Weiss8fb719e2018-03-30 21:26:04 +0200438 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200439 private void WriteJSONString(byte[] b)
440 {
441 context.Write();
442 trans.Write(QUOTE);
443 int len = b.Length;
444 for (int i = 0; i < len; i++)
445 {
446 if ((b[i] & 0x00FF) >= 0x30)
447 {
448 if (b[i] == BACKSLASH[0])
449 {
450 trans.Write(BACKSLASH);
451 trans.Write(BACKSLASH);
452 }
453 else
454 {
455 trans.Write(b, i, 1);
456 }
457 }
458 else
459 {
460 tempBuffer[0] = JSON_CHAR_TABLE[b[i]];
461 if (tempBuffer[0] == 1)
462 {
463 trans.Write(b, i, 1);
464 }
465 else if (tempBuffer[0] > 1)
466 {
467 trans.Write(BACKSLASH);
468 trans.Write(tempBuffer, 0, 1);
469 }
470 else
471 {
472 trans.Write(ESCSEQ);
473 tempBuffer[0] = HexChar((byte)(b[i] >> 4));
474 tempBuffer[1] = HexChar(b[i]);
475 trans.Write(tempBuffer, 0, 2);
476 }
477 }
478 }
479 trans.Write(QUOTE);
480 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000481
Christian Weiss8fb719e2018-03-30 21:26:04 +0200482 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200483 /// Write out number as a JSON value. If the context dictates so, it will be
484 /// wrapped in quotes to output as a JSON string.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200485 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200486 private void WriteJSONInteger(long num)
487 {
488 context.Write();
Christian Weiss8fb719e2018-03-30 21:26:04 +0200489 string str = num.ToString();
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000490
Jens Geyerd5436f52014-10-03 19:50:38 +0200491 bool escapeNum = context.EscapeNumbers();
492 if (escapeNum)
493 trans.Write(QUOTE);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000494
Jens Geyerd5436f52014-10-03 19:50:38 +0200495 trans.Write(utf8Encoding.GetBytes(str));
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000496
Jens Geyerd5436f52014-10-03 19:50:38 +0200497 if (escapeNum)
498 trans.Write(QUOTE);
499 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000500
Christian Weiss8fb719e2018-03-30 21:26:04 +0200501 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200502 /// Write out a double as a JSON value. If it is NaN or infinity or if the
503 /// context dictates escaping, Write out as JSON string.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200504 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200505 private void WriteJSONDouble(double num)
506 {
507 context.Write();
Christian Weiss8fb719e2018-03-30 21:26:04 +0200508 string str = num.ToString("G17", CultureInfo.InvariantCulture);
Jens Geyerd5436f52014-10-03 19:50:38 +0200509 bool special = false;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000510
Jens Geyerd5436f52014-10-03 19:50:38 +0200511 switch (str[0])
512 {
513 case 'N': // NaN
514 case 'I': // Infinity
515 special = true;
516 break;
517 case '-':
518 if (str[1] == 'I')
519 { // -Infinity
520 special = true;
521 }
522 break;
523 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000524
Jens Geyerd5436f52014-10-03 19:50:38 +0200525 bool escapeNum = special || context.EscapeNumbers();
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000526
Jens Geyerd5436f52014-10-03 19:50:38 +0200527 if (escapeNum)
528 trans.Write(QUOTE);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000529
Jens Geyerd5436f52014-10-03 19:50:38 +0200530 trans.Write(utf8Encoding.GetBytes(str));
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000531
Jens Geyerd5436f52014-10-03 19:50:38 +0200532 if (escapeNum)
533 trans.Write(QUOTE);
534 }
Christian Weiss8fb719e2018-03-30 21:26:04 +0200535 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200536 /// Write out contents of byte array b as a JSON string with base-64 encoded
537 /// data
Christian Weiss8fb719e2018-03-30 21:26:04 +0200538 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200539 private void WriteJSONBase64(byte[] b)
540 {
541 context.Write();
542 trans.Write(QUOTE);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000543
Jens Geyerd5436f52014-10-03 19:50:38 +0200544 int len = b.Length;
545 int off = 0;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000546
Jens Geyerd5436f52014-10-03 19:50:38 +0200547 while (len >= 3)
548 {
549 // Encode 3 bytes at a time
550 TBase64Utils.encode(b, off, 3, tempBuffer, 0);
551 trans.Write(tempBuffer, 0, 4);
552 off += 3;
553 len -= 3;
554 }
555 if (len > 0)
556 {
557 // Encode remainder
558 TBase64Utils.encode(b, off, len, tempBuffer, 0);
559 trans.Write(tempBuffer, 0, len + 1);
560 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000561
Jens Geyerd5436f52014-10-03 19:50:38 +0200562 trans.Write(QUOTE);
563 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000564
Jens Geyerd5436f52014-10-03 19:50:38 +0200565 private void WriteJSONObjectStart()
566 {
567 context.Write();
568 trans.Write(LBRACE);
569 PushContext(new JSONPairContext(this));
570 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000571
Jens Geyerd5436f52014-10-03 19:50:38 +0200572 private void WriteJSONObjectEnd()
573 {
574 PopContext();
575 trans.Write(RBRACE);
576 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000577
Jens Geyerd5436f52014-10-03 19:50:38 +0200578 private void WriteJSONArrayStart()
579 {
580 context.Write();
581 trans.Write(LBRACKET);
582 PushContext(new JSONListContext(this));
583 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000584
Jens Geyerd5436f52014-10-03 19:50:38 +0200585 private void WriteJSONArrayEnd()
586 {
587 PopContext();
588 trans.Write(RBRACKET);
589 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000590
Jens Geyerd5436f52014-10-03 19:50:38 +0200591 public override void WriteMessageBegin(TMessage message)
592 {
593 WriteJSONArrayStart();
594 WriteJSONInteger(VERSION);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000595
Jens Geyerd5436f52014-10-03 19:50:38 +0200596 byte[] b = utf8Encoding.GetBytes(message.Name);
597 WriteJSONString(b);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000598
Jens Geyerd5436f52014-10-03 19:50:38 +0200599 WriteJSONInteger((long)message.Type);
600 WriteJSONInteger(message.SeqID);
601 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000602
Jens Geyerd5436f52014-10-03 19:50:38 +0200603 public override void WriteMessageEnd()
604 {
605 WriteJSONArrayEnd();
606 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000607
Jens Geyerd5436f52014-10-03 19:50:38 +0200608 public override void WriteStructBegin(TStruct str)
609 {
610 WriteJSONObjectStart();
611 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000612
Jens Geyerd5436f52014-10-03 19:50:38 +0200613 public override void WriteStructEnd()
614 {
615 WriteJSONObjectEnd();
616 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000617
Jens Geyerd5436f52014-10-03 19:50:38 +0200618 public override void WriteFieldBegin(TField field)
619 {
620 WriteJSONInteger(field.ID);
621 WriteJSONObjectStart();
622 WriteJSONString(GetTypeNameForTypeID(field.Type));
623 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000624
Jens Geyerd5436f52014-10-03 19:50:38 +0200625 public override void WriteFieldEnd()
626 {
627 WriteJSONObjectEnd();
628 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000629
Jens Geyerd5436f52014-10-03 19:50:38 +0200630 public override void WriteFieldStop() { }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000631
Jens Geyerd5436f52014-10-03 19:50:38 +0200632 public override void WriteMapBegin(TMap map)
633 {
634 WriteJSONArrayStart();
635 WriteJSONString(GetTypeNameForTypeID(map.KeyType));
636 WriteJSONString(GetTypeNameForTypeID(map.ValueType));
637 WriteJSONInteger(map.Count);
638 WriteJSONObjectStart();
639 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000640
Jens Geyerd5436f52014-10-03 19:50:38 +0200641 public override void WriteMapEnd()
642 {
643 WriteJSONObjectEnd();
644 WriteJSONArrayEnd();
645 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000646
Jens Geyerd5436f52014-10-03 19:50:38 +0200647 public override void WriteListBegin(TList list)
648 {
649 WriteJSONArrayStart();
650 WriteJSONString(GetTypeNameForTypeID(list.ElementType));
651 WriteJSONInteger(list.Count);
652 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000653
Jens Geyerd5436f52014-10-03 19:50:38 +0200654 public override void WriteListEnd()
655 {
656 WriteJSONArrayEnd();
657 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000658
Jens Geyerd5436f52014-10-03 19:50:38 +0200659 public override void WriteSetBegin(TSet set)
660 {
661 WriteJSONArrayStart();
662 WriteJSONString(GetTypeNameForTypeID(set.ElementType));
663 WriteJSONInteger(set.Count);
664 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000665
Jens Geyerd5436f52014-10-03 19:50:38 +0200666 public override void WriteSetEnd()
667 {
668 WriteJSONArrayEnd();
669 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000670
Jens Geyerd5436f52014-10-03 19:50:38 +0200671 public override void WriteBool(bool b)
672 {
673 WriteJSONInteger(b ? (long)1 : (long)0);
674 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000675
Jens Geyerd5436f52014-10-03 19:50:38 +0200676 public override void WriteByte(sbyte b)
677 {
678 WriteJSONInteger((long)b);
679 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000680
Jens Geyerd5436f52014-10-03 19:50:38 +0200681 public override void WriteI16(short i16)
682 {
683 WriteJSONInteger((long)i16);
684 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000685
Jens Geyerd5436f52014-10-03 19:50:38 +0200686 public override void WriteI32(int i32)
687 {
688 WriteJSONInteger((long)i32);
689 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000690
Jens Geyerd5436f52014-10-03 19:50:38 +0200691 public override void WriteI64(long i64)
692 {
693 WriteJSONInteger(i64);
694 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000695
Jens Geyerd5436f52014-10-03 19:50:38 +0200696 public override void WriteDouble(double dub)
697 {
698 WriteJSONDouble(dub);
699 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000700
Christian Weiss8fb719e2018-03-30 21:26:04 +0200701 public override void WriteString(string str)
Jens Geyerd5436f52014-10-03 19:50:38 +0200702 {
703 byte[] b = utf8Encoding.GetBytes(str);
704 WriteJSONString(b);
705 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000706
Jens Geyerd5436f52014-10-03 19:50:38 +0200707 public override void WriteBinary(byte[] bin)
708 {
709 WriteJSONBase64(bin);
710 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000711
Jens Geyerd5436f52014-10-03 19:50:38 +0200712 /**
713 * Reading methods.
714 */
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000715
Christian Weiss8fb719e2018-03-30 21:26:04 +0200716 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200717 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
718 /// context if skipContext is true.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200719 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200720 private byte[] ReadJSONString(bool skipContext)
721 {
722 MemoryStream buffer = new MemoryStream();
Phongphan Phuttha11b515c2015-10-30 01:31:44 +0700723 List<char> codeunits = new List<char>();
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000724
725
Jens Geyerd5436f52014-10-03 19:50:38 +0200726 if (!skipContext)
727 {
728 context.Read();
729 }
730 ReadJSONSyntaxChar(QUOTE);
731 while (true)
732 {
733 byte ch = reader.Read();
734 if (ch == QUOTE[0])
735 {
736 break;
737 }
Jens Geyer73938622014-02-07 22:22:36 +0100738
Jens Geyerd5436f52014-10-03 19:50:38 +0200739 // escaped?
740 if (ch != ESCSEQ[0])
741 {
742 buffer.Write(new byte[] { (byte)ch }, 0, 1);
743 continue;
744 }
Jens Geyer73938622014-02-07 22:22:36 +0100745
Jens Geyerd5436f52014-10-03 19:50:38 +0200746 // distinguish between \uXXXX and \?
747 ch = reader.Read();
748 if (ch != ESCSEQ[1]) // control chars like \n
749 {
750 int off = Array.IndexOf(ESCAPE_CHARS, (char)ch);
751 if (off == -1)
752 {
753 throw new TProtocolException(TProtocolException.INVALID_DATA,
754 "Expected control char");
755 }
756 ch = ESCAPE_CHAR_VALS[off];
757 buffer.Write(new byte[] { (byte)ch }, 0, 1);
758 continue;
759 }
Jens Geyer06ad7212014-02-16 15:48:57 +0100760
761
Jens Geyerd5436f52014-10-03 19:50:38 +0200762 // it's \uXXXX
763 trans.ReadAll(tempBuffer, 0, 4);
764 var wch = (short)((HexVal((byte)tempBuffer[0]) << 12) +
765 (HexVal((byte)tempBuffer[1]) << 8) +
766 (HexVal((byte)tempBuffer[2]) << 4) +
767 HexVal(tempBuffer[3]));
Phongphan Phuttha11b515c2015-10-30 01:31:44 +0700768 if (Char.IsHighSurrogate((char)wch))
769 {
770 if (codeunits.Count > 0)
771 {
772 throw new TProtocolException(TProtocolException.INVALID_DATA,
773 "Expected low surrogate char");
774 }
775 codeunits.Add((char)wch);
776 }
777 else if (Char.IsLowSurrogate((char)wch))
778 {
779 if (codeunits.Count == 0)
780 {
781 throw new TProtocolException(TProtocolException.INVALID_DATA,
782 "Expected high surrogate char");
783 }
784 codeunits.Add((char)wch);
785 var tmp = utf8Encoding.GetBytes(codeunits.ToArray());
786 buffer.Write(tmp, 0, tmp.Length);
787 codeunits.Clear();
788 }
789 else
790 {
791 var tmp = utf8Encoding.GetBytes(new char[] { (char)wch });
792 buffer.Write(tmp, 0, tmp.Length);
793 }
Jens Geyerd5436f52014-10-03 19:50:38 +0200794 }
Phongphan Phuttha11b515c2015-10-30 01:31:44 +0700795
796
797 if (codeunits.Count > 0)
798 {
799 throw new TProtocolException(TProtocolException.INVALID_DATA,
800 "Expected low surrogate char");
801 }
802
Jens Geyerd5436f52014-10-03 19:50:38 +0200803 return buffer.ToArray();
804 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000805
Christian Weiss8fb719e2018-03-30 21:26:04 +0200806 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200807 /// Return true if the given byte could be a valid part of a JSON number.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200808 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200809 private bool IsJSONNumeric(byte b)
810 {
811 switch (b)
812 {
813 case (byte)'+':
814 case (byte)'-':
815 case (byte)'.':
816 case (byte)'0':
817 case (byte)'1':
818 case (byte)'2':
819 case (byte)'3':
820 case (byte)'4':
821 case (byte)'5':
822 case (byte)'6':
823 case (byte)'7':
824 case (byte)'8':
825 case (byte)'9':
826 case (byte)'E':
827 case (byte)'e':
828 return true;
829 }
830 return false;
831 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000832
Christian Weiss8fb719e2018-03-30 21:26:04 +0200833 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200834 /// Read in a sequence of characters that are all valid in JSON numbers. Does
835 /// not do a complete regex check to validate that this is actually a number.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200836 /// </summary>
837 private string ReadJSONNumericChars()
Jens Geyerd5436f52014-10-03 19:50:38 +0200838 {
839 StringBuilder strbld = new StringBuilder();
840 while (true)
841 {
842 byte ch = reader.Peek();
843 if (!IsJSONNumeric(ch))
844 {
845 break;
846 }
847 strbld.Append((char)reader.Read());
848 }
849 return strbld.ToString();
850 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000851
Christian Weiss8fb719e2018-03-30 21:26:04 +0200852 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200853 /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200854 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200855 private long ReadJSONInteger()
856 {
857 context.Read();
858 if (context.EscapeNumbers())
859 {
860 ReadJSONSyntaxChar(QUOTE);
861 }
Jens Geyer6e67faa2018-08-06 23:31:38 +0200862
Christian Weiss8fb719e2018-03-30 21:26:04 +0200863 string str = ReadJSONNumericChars();
Jens Geyerd5436f52014-10-03 19:50:38 +0200864 if (context.EscapeNumbers())
865 {
866 ReadJSONSyntaxChar(QUOTE);
867 }
Jens Geyer6e67faa2018-08-06 23:31:38 +0200868
Jens Geyerd5436f52014-10-03 19:50:38 +0200869 try
870 {
871 return Int64.Parse(str);
872 }
Jens Geyer6e67faa2018-08-06 23:31:38 +0200873 catch (FormatException fex)
Jens Geyerd5436f52014-10-03 19:50:38 +0200874 {
875 throw new TProtocolException(TProtocolException.INVALID_DATA,
Jens Geyer6e67faa2018-08-06 23:31:38 +0200876 "Bad data encounted in numeric data", fex);
Jens Geyerd5436f52014-10-03 19:50:38 +0200877 }
878 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000879
Christian Weiss8fb719e2018-03-30 21:26:04 +0200880 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200881 /// Read in a JSON double value. Throw if the value is not wrapped in quotes
882 /// when expected or if wrapped in quotes when not expected.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200883 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200884 private double ReadJSONDouble()
885 {
886 context.Read();
887 if (reader.Peek() == QUOTE[0])
888 {
889 byte[] arr = ReadJSONString(true);
Christian Weiss8fb719e2018-03-30 21:26:04 +0200890 double dub = Double.Parse(utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000891
Jens Geyer6e67faa2018-08-06 23:31:38 +0200892 if (!context.EscapeNumbers() && !Double.IsNaN(dub) && !Double.IsInfinity(dub))
Jens Geyerd5436f52014-10-03 19:50:38 +0200893 {
894 // Throw exception -- we should not be in a string in this case
895 throw new TProtocolException(TProtocolException.INVALID_DATA,
896 "Numeric data unexpectedly quoted");
897 }
898 return dub;
899 }
900 else
901 {
902 if (context.EscapeNumbers())
903 {
904 // This will throw - we should have had a quote if escapeNum == true
905 ReadJSONSyntaxChar(QUOTE);
906 }
907 try
908 {
909 return Double.Parse(ReadJSONNumericChars(), CultureInfo.InvariantCulture);
910 }
Jens Geyer6e67faa2018-08-06 23:31:38 +0200911 catch (FormatException fex)
Jens Geyerd5436f52014-10-03 19:50:38 +0200912 {
913 throw new TProtocolException(TProtocolException.INVALID_DATA,
Jens Geyer6e67faa2018-08-06 23:31:38 +0200914 "Bad data encounted in numeric data", fex);
Jens Geyerd5436f52014-10-03 19:50:38 +0200915 }
916 }
917 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000918
Christian Weiss8fb719e2018-03-30 21:26:04 +0200919 /// <summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200920 /// Read in a JSON string containing base-64 encoded data and decode it.
Christian Weiss8fb719e2018-03-30 21:26:04 +0200921 /// </summary>
Jens Geyerd5436f52014-10-03 19:50:38 +0200922 private byte[] ReadJSONBase64()
923 {
924 byte[] b = ReadJSONString(false);
925 int len = b.Length;
926 int off = 0;
927 int size = 0;
928 // reduce len to ignore fill bytes
929 while ((len > 0) && (b[len - 1] == '='))
930 {
931 --len;
932 }
933 // read & decode full byte triplets = 4 source bytes
934 while (len > 4)
935 {
936 // Decode 4 bytes at a time
937 TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
938 off += 4;
939 len -= 4;
940 size += 3;
941 }
942 // Don't decode if we hit the end or got a single leftover byte (invalid
943 // base64 but legal for skip of regular string type)
944 if (len > 1)
945 {
946 // Decode remainder
947 TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
948 size += len - 1;
949 }
950 // Sadly we must copy the byte[] (any way around this?)
951 byte[] result = new byte[size];
952 Array.Copy(b, 0, result, 0, size);
953 return result;
954 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000955
Jens Geyerd5436f52014-10-03 19:50:38 +0200956 private void ReadJSONObjectStart()
957 {
958 context.Read();
959 ReadJSONSyntaxChar(LBRACE);
960 PushContext(new JSONPairContext(this));
961 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000962
Jens Geyerd5436f52014-10-03 19:50:38 +0200963 private void ReadJSONObjectEnd()
964 {
965 ReadJSONSyntaxChar(RBRACE);
966 PopContext();
967 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000968
Jens Geyerd5436f52014-10-03 19:50:38 +0200969 private void ReadJSONArrayStart()
970 {
971 context.Read();
972 ReadJSONSyntaxChar(LBRACKET);
973 PushContext(new JSONListContext(this));
974 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000975
Jens Geyerd5436f52014-10-03 19:50:38 +0200976 private void ReadJSONArrayEnd()
977 {
978 ReadJSONSyntaxChar(RBRACKET);
979 PopContext();
980 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000981
Jens Geyerd5436f52014-10-03 19:50:38 +0200982 public override TMessage ReadMessageBegin()
983 {
984 TMessage message = new TMessage();
985 ReadJSONArrayStart();
986 if (ReadJSONInteger() != VERSION)
987 {
988 throw new TProtocolException(TProtocolException.BAD_VERSION,
989 "Message contained bad version.");
990 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000991
Roger Meier284a9b52011-12-08 13:39:56 +0000992 var buf = ReadJSONString(false);
Christian Weiss8fb719e2018-03-30 21:26:04 +0200993 message.Name = utf8Encoding.GetString(buf, 0, buf.Length);
Jens Geyerd5436f52014-10-03 19:50:38 +0200994 message.Type = (TMessageType)ReadJSONInteger();
995 message.SeqID = (int)ReadJSONInteger();
996 return message;
997 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000998
Jens Geyerd5436f52014-10-03 19:50:38 +0200999 public override void ReadMessageEnd()
1000 {
1001 ReadJSONArrayEnd();
1002 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001003
Jens Geyerd5436f52014-10-03 19:50:38 +02001004 public override TStruct ReadStructBegin()
1005 {
1006 ReadJSONObjectStart();
1007 return new TStruct();
1008 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001009
Jens Geyerd5436f52014-10-03 19:50:38 +02001010 public override void ReadStructEnd()
1011 {
1012 ReadJSONObjectEnd();
1013 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001014
Jens Geyerd5436f52014-10-03 19:50:38 +02001015 public override TField ReadFieldBegin()
1016 {
1017 TField field = new TField();
1018 byte ch = reader.Peek();
1019 if (ch == RBRACE[0])
1020 {
1021 field.Type = TType.Stop;
1022 }
1023 else
1024 {
1025 field.ID = (short)ReadJSONInteger();
1026 ReadJSONObjectStart();
1027 field.Type = GetTypeIDForTypeName(ReadJSONString(false));
1028 }
1029 return field;
1030 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001031
Jens Geyerd5436f52014-10-03 19:50:38 +02001032 public override void ReadFieldEnd()
1033 {
1034 ReadJSONObjectEnd();
1035 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001036
Jens Geyerd5436f52014-10-03 19:50:38 +02001037 public override TMap ReadMapBegin()
1038 {
1039 TMap map = new TMap();
1040 ReadJSONArrayStart();
1041 map.KeyType = GetTypeIDForTypeName(ReadJSONString(false));
1042 map.ValueType = GetTypeIDForTypeName(ReadJSONString(false));
1043 map.Count = (int)ReadJSONInteger();
1044 ReadJSONObjectStart();
1045 return map;
1046 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001047
Jens Geyerd5436f52014-10-03 19:50:38 +02001048 public override void ReadMapEnd()
1049 {
1050 ReadJSONObjectEnd();
1051 ReadJSONArrayEnd();
1052 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001053
Jens Geyerd5436f52014-10-03 19:50:38 +02001054 public override TList ReadListBegin()
1055 {
1056 TList list = new TList();
1057 ReadJSONArrayStart();
1058 list.ElementType = GetTypeIDForTypeName(ReadJSONString(false));
1059 list.Count = (int)ReadJSONInteger();
1060 return list;
1061 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001062
Jens Geyerd5436f52014-10-03 19:50:38 +02001063 public override void ReadListEnd()
1064 {
1065 ReadJSONArrayEnd();
1066 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001067
Jens Geyerd5436f52014-10-03 19:50:38 +02001068 public override TSet ReadSetBegin()
1069 {
1070 TSet set = new TSet();
1071 ReadJSONArrayStart();
1072 set.ElementType = GetTypeIDForTypeName(ReadJSONString(false));
1073 set.Count = (int)ReadJSONInteger();
1074 return set;
1075 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001076
Jens Geyerd5436f52014-10-03 19:50:38 +02001077 public override void ReadSetEnd()
1078 {
1079 ReadJSONArrayEnd();
1080 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001081
Jens Geyerd5436f52014-10-03 19:50:38 +02001082 public override bool ReadBool()
1083 {
1084 return (ReadJSONInteger() == 0 ? false : true);
1085 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001086
Jens Geyerd5436f52014-10-03 19:50:38 +02001087 public override sbyte ReadByte()
1088 {
1089 return (sbyte)ReadJSONInteger();
1090 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001091
Jens Geyerd5436f52014-10-03 19:50:38 +02001092 public override short ReadI16()
1093 {
1094 return (short)ReadJSONInteger();
1095 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001096
Jens Geyerd5436f52014-10-03 19:50:38 +02001097 public override int ReadI32()
1098 {
1099 return (int)ReadJSONInteger();
1100 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001101
Jens Geyerd5436f52014-10-03 19:50:38 +02001102 public override long ReadI64()
1103 {
1104 return (long)ReadJSONInteger();
1105 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001106
Jens Geyerd5436f52014-10-03 19:50:38 +02001107 public override double ReadDouble()
1108 {
1109 return ReadJSONDouble();
1110 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001111
Christian Weiss8fb719e2018-03-30 21:26:04 +02001112 public override string ReadString()
Jens Geyerd5436f52014-10-03 19:50:38 +02001113 {
Roger Meier284a9b52011-12-08 13:39:56 +00001114 var buf = ReadJSONString(false);
Christian Weiss8fb719e2018-03-30 21:26:04 +02001115 return utf8Encoding.GetString(buf, 0, buf.Length);
Jens Geyerd5436f52014-10-03 19:50:38 +02001116 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001117
Jens Geyerd5436f52014-10-03 19:50:38 +02001118 public override byte[] ReadBinary()
1119 {
1120 return ReadJSONBase64();
1121 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001122
Jens Geyerd5436f52014-10-03 19:50:38 +02001123 }
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001124}