1 // Licensed to the Apache Software Foundation(ASF) under one
2 // or more contributor license agreements.See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership.The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing,
12 // software distributed under the License is distributed on an
13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, either express or implied. See the License for the
15 // specific language governing permissions and limitations
19 using System.Collections.Generic;
20 using System.Globalization;
24 using System.Threading;
25 using System.Threading.Tasks;
26 using Thrift.Protocol.Entities;
27 using Thrift.Protocol.Utilities;
28 using Thrift.Transport;
30 namespace Thrift.Protocol
33 /// JSON protocol implementation for thrift.
34 /// This is a full-featured protocol supporting Write and Read.
35 /// Please see the C++ class header for a detailed description of the
36 /// protocol's wire format.
37 /// Adapted from the Java version.
39 // ReSharper disable once InconsistentNaming
40 public class TJsonProtocol : TProtocol
42 private const long Version = 1;
44 // Temporary buffer used by several methods
45 private readonly byte[] _tempBuffer = new byte[4];
47 // Current context that we are in
48 protected JSONBaseContext Context;
50 // Stack of nested contexts that we may be in
51 protected Stack<JSONBaseContext> ContextStack = new Stack<JSONBaseContext>();
53 // Reader that manages a 1-byte buffer
54 protected LookaheadReader Reader;
57 protected Encoding Utf8Encoding = Encoding.UTF8;
60 /// TJsonProtocol Constructor
62 public TJsonProtocol(TTransport trans)
65 Context = new JSONBaseContext(this);
66 Reader = new LookaheadReader(this);
70 /// Push a new JSON context onto the stack.
72 protected void PushContext(JSONBaseContext c)
74 ContextStack.Push(Context);
79 /// Pop the last JSON context off the stack
81 protected void PopContext()
83 Context = ContextStack.Pop();
87 /// Read a byte that must match b[0]; otherwise an exception is thrown.
88 /// Marked protected to avoid synthetic accessor in JSONListContext.Read
89 /// and JSONPairContext.Read
91 protected async Task ReadJsonSyntaxCharAsync(byte[] bytes, CancellationToken cancellationToken)
93 var ch = await Reader.ReadAsync(cancellationToken);
96 throw new TProtocolException(TProtocolException.INVALID_DATA, $"Unexpected character: {(char) ch}");
101 /// Write the bytes in array buf as a JSON characters, escaping as needed
103 private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken)
105 await Context.WriteConditionalDelimiterAsync(cancellationToken);
106 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
108 var len = bytes.Length;
109 for (var i = 0; i < len; i++)
111 if ((bytes[i] & 0x00FF) >= 0x30)
113 if (bytes[i] == TJSONProtocolConstants.Backslash[0])
115 await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
116 await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
120 await Trans.WriteAsync(bytes.ToArray(), i, 1, cancellationToken);
125 _tempBuffer[0] = TJSONProtocolConstants.JsonCharTable[bytes[i]];
126 if (_tempBuffer[0] == 1)
128 await Trans.WriteAsync(bytes, i, 1, cancellationToken);
130 else if (_tempBuffer[0] > 1)
132 await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
133 await Trans.WriteAsync(_tempBuffer, 0, 1, cancellationToken);
137 await Trans.WriteAsync(TJSONProtocolConstants.EscSequences, cancellationToken);
138 _tempBuffer[0] = TJSONProtocolHelper.ToHexChar((byte) (bytes[i] >> 4));
139 _tempBuffer[1] = TJSONProtocolHelper.ToHexChar(bytes[i]);
140 await Trans.WriteAsync(_tempBuffer, 0, 2, cancellationToken);
144 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
148 /// Write out number as a JSON value. If the context dictates so, it will be
149 /// wrapped in quotes to output as a JSON string.
151 private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellationToken)
153 await Context.WriteConditionalDelimiterAsync(cancellationToken);
154 var str = num.ToString();
156 var escapeNum = Context.EscapeNumbers();
159 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
162 var bytes = Utf8Encoding.GetBytes(str);
163 await Trans.WriteAsync(bytes, cancellationToken);
167 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
172 /// Write out a double as a JSON value. If it is NaN or infinity or if the
173 /// context dictates escaping, Write out as JSON string.
175 private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellationToken)
177 await Context.WriteConditionalDelimiterAsync(cancellationToken);
178 var str = num.ToString("G17", CultureInfo.InvariantCulture);
184 case 'I': // Infinity
196 var escapeNum = special || Context.EscapeNumbers();
200 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
203 await Trans.WriteAsync(Utf8Encoding.GetBytes(str), cancellationToken);
207 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
212 /// Write out contents of byte array b as a JSON string with base-64 encoded
215 private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken)
217 await Context.WriteConditionalDelimiterAsync(cancellationToken);
218 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
220 var len = bytes.Length;
225 // Encode 3 bytes at a time
226 TBase64Utils.Encode(bytes, off, 3, _tempBuffer, 0);
227 await Trans.WriteAsync(_tempBuffer, 0, 4, cancellationToken);
235 TBase64Utils.Encode(bytes, off, len, _tempBuffer, 0);
236 await Trans.WriteAsync(_tempBuffer, 0, len + 1, cancellationToken);
239 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
242 private async Task WriteJsonObjectStartAsync(CancellationToken cancellationToken)
244 await Context.WriteConditionalDelimiterAsync(cancellationToken);
245 await Trans.WriteAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
246 PushContext(new JSONPairContext(this));
249 private async Task WriteJsonObjectEndAsync(CancellationToken cancellationToken)
252 await Trans.WriteAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
255 private async Task WriteJsonArrayStartAsync(CancellationToken cancellationToken)
257 await Context.WriteConditionalDelimiterAsync(cancellationToken);
258 await Trans.WriteAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
259 PushContext(new JSONListContext(this));
262 private async Task WriteJsonArrayEndAsync(CancellationToken cancellationToken)
265 await Trans.WriteAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
268 public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
270 await WriteJsonArrayStartAsync(cancellationToken);
271 await WriteJsonIntegerAsync(Version, cancellationToken);
273 var b = Utf8Encoding.GetBytes(message.Name);
274 await WriteJsonStringAsync(b, cancellationToken);
276 await WriteJsonIntegerAsync((long) message.Type, cancellationToken);
277 await WriteJsonIntegerAsync(message.SeqID, cancellationToken);
280 public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
282 await WriteJsonArrayEndAsync(cancellationToken);
285 public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
287 await WriteJsonObjectStartAsync(cancellationToken);
290 public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
292 await WriteJsonObjectEndAsync(cancellationToken);
295 public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
297 await WriteJsonIntegerAsync(field.ID, cancellationToken);
298 await WriteJsonObjectStartAsync(cancellationToken);
299 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(field.Type), cancellationToken);
302 public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
304 await WriteJsonObjectEndAsync(cancellationToken);
307 public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
309 if (cancellationToken.IsCancellationRequested)
311 await Task.FromCanceled(cancellationToken);
315 public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
317 await WriteJsonArrayStartAsync(cancellationToken);
318 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.KeyType), cancellationToken);
319 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.ValueType), cancellationToken);
320 await WriteJsonIntegerAsync(map.Count, cancellationToken);
321 await WriteJsonObjectStartAsync(cancellationToken);
324 public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
326 await WriteJsonObjectEndAsync(cancellationToken);
327 await WriteJsonArrayEndAsync(cancellationToken);
330 public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
332 await WriteJsonArrayStartAsync(cancellationToken);
333 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(list.ElementType), cancellationToken);
334 await WriteJsonIntegerAsync(list.Count, cancellationToken);
337 public override async Task WriteListEndAsync(CancellationToken cancellationToken)
339 await WriteJsonArrayEndAsync(cancellationToken);
342 public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
344 await WriteJsonArrayStartAsync(cancellationToken);
345 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(set.ElementType), cancellationToken);
346 await WriteJsonIntegerAsync(set.Count, cancellationToken);
349 public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
351 await WriteJsonArrayEndAsync(cancellationToken);
354 public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
356 await WriteJsonIntegerAsync(b ? 1 : 0, cancellationToken);
359 public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
361 await WriteJsonIntegerAsync(b, cancellationToken);
364 public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
366 await WriteJsonIntegerAsync(i16, cancellationToken);
369 public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
371 await WriteJsonIntegerAsync(i32, cancellationToken);
374 public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
376 await WriteJsonIntegerAsync(i64, cancellationToken);
379 public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
381 await WriteJsonDoubleAsync(d, cancellationToken);
384 public override async Task WriteStringAsync(string s, CancellationToken cancellationToken)
386 var b = Utf8Encoding.GetBytes(s);
387 await WriteJsonStringAsync(b, cancellationToken);
390 public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
392 await WriteJsonBase64Async(bytes, cancellationToken);
396 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
397 /// context if skipContext is true.
399 private async ValueTask<byte[]> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)
401 using (var buffer = new MemoryStream())
403 var codeunits = new List<char>();
408 await Context.ReadConditionalDelimiterAsync(cancellationToken);
411 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
415 var ch = await Reader.ReadAsync(cancellationToken);
416 if (ch == TJSONProtocolConstants.Quote[0])
422 if (ch != TJSONProtocolConstants.EscSequences[0])
424 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
428 // distinguish between \uXXXX and \?
429 ch = await Reader.ReadAsync(cancellationToken);
430 if (ch != TJSONProtocolConstants.EscSequences[1]) // control chars like \n
432 var off = Array.IndexOf(TJSONProtocolConstants.EscapeChars, (char) ch);
435 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected control char");
437 ch = TJSONProtocolConstants.EscapeCharValues[off];
438 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
443 await Trans.ReadAllAsync(_tempBuffer, 0, 4, cancellationToken);
445 var wch = (short) ((TJSONProtocolHelper.ToHexVal(_tempBuffer[0]) << 12) +
446 (TJSONProtocolHelper.ToHexVal(_tempBuffer[1]) << 8) +
447 (TJSONProtocolHelper.ToHexVal(_tempBuffer[2]) << 4) +
448 TJSONProtocolHelper.ToHexVal(_tempBuffer[3]));
450 if (char.IsHighSurrogate((char) wch))
452 if (codeunits.Count > 0)
454 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
456 codeunits.Add((char) wch);
458 else if (char.IsLowSurrogate((char) wch))
460 if (codeunits.Count == 0)
462 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected high surrogate char");
465 codeunits.Add((char) wch);
466 var tmp = Utf8Encoding.GetBytes(codeunits.ToArray());
467 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
472 var tmp = Utf8Encoding.GetBytes(new[] {(char) wch});
473 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
477 if (codeunits.Count > 0)
479 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
482 return buffer.ToArray();
487 /// Read in a sequence of characters that are all valid in JSON numbers. Does
488 /// not do a complete regex check to validate that this is actually a number.
490 private async ValueTask<string> ReadJsonNumericCharsAsync(CancellationToken cancellationToken)
492 var strbld = new StringBuilder();
495 //TODO: workaround for primitive types with TJsonProtocol, think - how to rewrite into more easy form without exceptions
498 var ch = await Reader.PeekAsync(cancellationToken);
499 if (!TJSONProtocolHelper.IsJsonNumeric(ch))
503 var c = (char)await Reader.ReadAsync(cancellationToken);
506 catch (TTransportException)
511 return strbld.ToString();
515 /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
517 private async ValueTask<long> ReadJsonIntegerAsync(CancellationToken cancellationToken)
519 await Context.ReadConditionalDelimiterAsync(cancellationToken);
520 if (Context.EscapeNumbers())
522 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
525 var str = await ReadJsonNumericCharsAsync(cancellationToken);
526 if (Context.EscapeNumbers())
528 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
533 return long.Parse(str);
535 catch (FormatException)
537 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
542 /// Read in a JSON double value. Throw if the value is not wrapped in quotes
543 /// when expected or if wrapped in quotes when not expected.
545 private async ValueTask<double> ReadJsonDoubleAsync(CancellationToken cancellationToken)
547 await Context.ReadConditionalDelimiterAsync(cancellationToken);
548 if (await Reader.PeekAsync(cancellationToken) == TJSONProtocolConstants.Quote[0])
550 var arr = await ReadJsonStringAsync(true, cancellationToken);
551 var dub = double.Parse(Utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture);
553 if (!Context.EscapeNumbers() && !double.IsNaN(dub) && !double.IsInfinity(dub))
555 // Throw exception -- we should not be in a string in this case
556 throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted");
562 if (Context.EscapeNumbers())
564 // This will throw - we should have had a quote if escapeNum == true
565 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
570 return double.Parse(await ReadJsonNumericCharsAsync(cancellationToken), CultureInfo.InvariantCulture);
572 catch (FormatException)
574 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
579 /// Read in a JSON string containing base-64 encoded data and decode it.
581 private async ValueTask<byte[]> ReadJsonBase64Async(CancellationToken cancellationToken)
583 var b = await ReadJsonStringAsync(false, cancellationToken);
588 // reduce len to ignore fill bytes
589 while ((len > 0) && (b[len - 1] == '='))
594 // read & decode full byte triplets = 4 source bytes
597 // Decode 4 bytes at a time
598 TBase64Utils.Decode(b, off, 4, b, size); // NB: decoded in place
604 // Don't decode if we hit the end or got a single leftover byte (invalid
605 // base64 but legal for skip of regular string exType)
609 TBase64Utils.Decode(b, off, len, b, size); // NB: decoded in place
613 // Sadly we must copy the byte[] (any way around this?)
614 var result = new byte[size];
615 Array.Copy(b, 0, result, 0, size);
619 private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken)
621 await Context.ReadConditionalDelimiterAsync(cancellationToken);
622 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
623 PushContext(new JSONPairContext(this));
626 private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken)
628 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
632 private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken)
634 await Context.ReadConditionalDelimiterAsync(cancellationToken);
635 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
636 PushContext(new JSONListContext(this));
639 private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken)
641 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
645 public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
647 var message = new TMessage();
648 await ReadJsonArrayStartAsync(cancellationToken);
649 if (await ReadJsonIntegerAsync(cancellationToken) != Version)
651 throw new TProtocolException(TProtocolException.BAD_VERSION, "Message contained bad version.");
654 var buf = await ReadJsonStringAsync(false, cancellationToken);
655 message.Name = Utf8Encoding.GetString(buf, 0, buf.Length);
656 message.Type = (TMessageType) await ReadJsonIntegerAsync(cancellationToken);
657 message.SeqID = (int) await ReadJsonIntegerAsync(cancellationToken);
661 public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
663 await ReadJsonArrayEndAsync(cancellationToken);
666 public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
668 await ReadJsonObjectStartAsync(cancellationToken);
669 return new TStruct();
672 public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
674 await ReadJsonObjectEndAsync(cancellationToken);
677 public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
679 var field = new TField();
680 var ch = await Reader.PeekAsync(cancellationToken);
681 if (ch == TJSONProtocolConstants.RightBrace[0])
683 field.Type = TType.Stop;
687 field.ID = (short) await ReadJsonIntegerAsync(cancellationToken);
688 await ReadJsonObjectStartAsync(cancellationToken);
689 field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
694 public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
696 await ReadJsonObjectEndAsync(cancellationToken);
699 public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
701 var map = new TMap();
702 await ReadJsonArrayStartAsync(cancellationToken);
703 map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
704 map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
705 map.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
706 await ReadJsonObjectStartAsync(cancellationToken);
710 public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
712 await ReadJsonObjectEndAsync(cancellationToken);
713 await ReadJsonArrayEndAsync(cancellationToken);
716 public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
718 var list = new TList();
719 await ReadJsonArrayStartAsync(cancellationToken);
720 list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
721 list.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
725 public override async Task ReadListEndAsync(CancellationToken cancellationToken)
727 await ReadJsonArrayEndAsync(cancellationToken);
730 public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
732 var set = new TSet();
733 await ReadJsonArrayStartAsync(cancellationToken);
734 set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
735 set.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
739 public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
741 await ReadJsonArrayEndAsync(cancellationToken);
744 public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
746 return await ReadJsonIntegerAsync(cancellationToken) != 0;
749 public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
751 return (sbyte) await ReadJsonIntegerAsync(cancellationToken);
754 public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
756 return (short) await ReadJsonIntegerAsync(cancellationToken);
759 public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
761 return (int) await ReadJsonIntegerAsync(cancellationToken);
764 public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
766 return await ReadJsonIntegerAsync(cancellationToken);
769 public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
771 return await ReadJsonDoubleAsync(cancellationToken);
774 public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
776 var buf = await ReadJsonStringAsync(false, cancellationToken);
777 return Utf8Encoding.GetString(buf, 0, buf.Length);
780 public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
782 return await ReadJsonBase64Async(cancellationToken);
786 /// Factory for JSON protocol objects
788 public class Factory : TProtocolFactory
790 public override TProtocol GetProtocol(TTransport trans)
792 return new TJsonProtocol(trans);
797 /// Base class for tracking JSON contexts that may require
798 /// inserting/Reading additional JSON syntax characters
799 /// This base context does nothing.
801 protected class JSONBaseContext
803 protected TJsonProtocol Proto;
805 public JSONBaseContext(TJsonProtocol proto)
810 public virtual async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
812 if (cancellationToken.IsCancellationRequested)
814 await Task.FromCanceled(cancellationToken);
818 public virtual async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
820 if (cancellationToken.IsCancellationRequested)
822 await Task.FromCanceled(cancellationToken);
826 public virtual bool EscapeNumbers()
833 /// Context for JSON lists. Will insert/Read commas before each item except
834 /// for the first one
836 protected class JSONListContext : JSONBaseContext
838 private bool _first = true;
840 public JSONListContext(TJsonProtocol protocol)
845 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
853 await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken);
857 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
865 await Proto.ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Comma, cancellationToken);
871 /// Context for JSON records. Will insert/Read colons before the value portion
872 /// of each record pair, and commas before each key except the first. In
873 /// addition, will indicate that numbers in the key position need to be
874 /// escaped in quotes (since JSON keys must be strings).
876 // ReSharper disable once InconsistentNaming
877 protected class JSONPairContext : JSONBaseContext
879 private bool _colon = true;
881 private bool _first = true;
883 public JSONPairContext(TJsonProtocol proto)
888 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
897 await Proto.Trans.WriteAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
902 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
911 await Proto.ReadJsonSyntaxCharAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
916 public override bool EscapeNumbers()
923 /// Holds up to one byte from the transport
925 protected class LookaheadReader
927 private readonly byte[] _data = new byte[1];
929 private bool _hasData;
930 protected TJsonProtocol Proto;
932 public LookaheadReader(TJsonProtocol proto)
938 /// Return and consume the next byte to be Read, either taking it from the
939 /// data buffer if present or getting it from the transport otherwise.
941 public async ValueTask<byte> ReadAsync(CancellationToken cancellationToken)
943 if (cancellationToken.IsCancellationRequested)
945 return await Task.FromCanceled<byte>(cancellationToken);
954 // find more easy way to avoid exception on reading primitive types
955 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
961 /// Return the next byte to be Read without consuming, filling the data
962 /// buffer if it has not been filled alReady.
964 public async ValueTask<byte> PeekAsync(CancellationToken cancellationToken)
966 if (cancellationToken.IsCancellationRequested)
968 return await Task.FromCanceled<byte>(cancellationToken);
973 // find more easy way to avoid exception on reading primitive types
974 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);