blob: 34338d5931d4e324cfaf326476ad945e849aa1b0 [file] [log] [blame]
Jake Farrell27274222011-11-10 20:32:44 +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
20{$SCOPEDENUMS ON}
21
22unit Thrift.Protocol.JSON;
23
24interface
25
26uses
27 Classes,
28 SysUtils,
29 Math,
30 IdCoderMIME,
31 Generics.Collections,
32 Thrift.Transport,
33 Thrift.Protocol;
34
35type
36 IJSONProtocol = interface( IProtocol)
37 ['{F0DAFDBD-692A-4B71-9736-F5D485A2178F}']
38 // Read a byte that must match b; otherwise an exception is thrown.
39 procedure ReadJSONSyntaxChar( b : Byte);
40 end;
41
42 // JSON protocol implementation for thrift.
43 // This is a full-featured protocol supporting Write and Read.
44 // Please see the C++ class header for a detailed description of the protocol's wire format.
45 // Adapted from the C# version.
46 TJSONProtocolImpl = class( TProtocolImpl, IJSONProtocol)
47 public
48 type
49 TFactory = class( TInterfacedObject, IProtocolFactory)
50 public
51 function GetProtocol( trans: ITransport): IProtocol;
52 end;
53
54 private
55 class function GetTypeNameForTypeID(typeID : TType) : string;
56 class function GetTypeIDForTypeName( const name : string) : TType;
57
58 protected
59 type
60 // Base class for tracking JSON contexts that may require
61 // inserting/Reading additional JSON syntax characters.
62 // This base context does nothing.
63 TJSONBaseContext = class
64 protected
65 FProto : IJSONProtocol;
66 public
67 constructor Create( const aProto : IJSONProtocol);
68 procedure Write; virtual;
69 procedure Read; virtual;
70 function EscapeNumbers : Boolean; virtual;
71 end;
72
73 // Context for JSON lists.
74 // Will insert/Read commas before each item except for the first one.
75 TJSONListContext = class( TJSONBaseContext)
76 private
77 FFirst : Boolean;
78 public
79 constructor Create( const aProto : IJSONProtocol);
80 procedure Write; override;
81 procedure Read; override;
82 end;
83
84 // Context for JSON records. Will insert/Read colons before the value portion of each record
85 // pair, and commas before each key except the first. In addition, will indicate that numbers
86 // in the key position need to be escaped in quotes (since JSON keys must be strings).
87 TJSONPairContext = class( TJSONBaseContext)
88 private
89 FFirst, FColon : Boolean;
90 public
91 constructor Create( const aProto : IJSONProtocol);
92 procedure Write; override;
93 procedure Read; override;
94 function EscapeNumbers : Boolean; override;
95 end;
96
97 // Holds up to one byte from the transport
98 TLookaheadReader = class
99 protected
100 FProto : IJSONProtocol;
101 constructor Create( const aProto : IJSONProtocol);
102
103 private
104 FHasData : Boolean;
105 FData : TBytes;
106
107 public
108 // Return and consume the next byte to be Read, either taking it from the
109 // data buffer if present or getting it from the transport otherwise.
110 function Read : Byte;
111
112 // Return the next byte to be Read without consuming, filling the data
113 // buffer if it has not been filled alReady.
114 function Peek : Byte;
115 end;
116
117 protected
118 // Stack of nested contexts that we may be in
119 FContextStack : TStack<TJSONBaseContext>;
120
121 // Current context that we are in
122 FContext : TJSONBaseContext;
123
124 // Reader that manages a 1-byte buffer
125 FReader : TLookaheadReader;
126
127 // Push/pop a new JSON context onto/from the stack.
128 procedure PushContext( aCtx : TJSONBaseContext);
129 procedure PopContext;
130
131 public
132 // TJSONProtocolImpl Constructor
133 constructor Create( aTrans : ITransport);
134 destructor Destroy; override;
135
136 protected
137 // IJSONProtocol
138 // Read a byte that must match b; otherwise an exception is thrown.
139 procedure ReadJSONSyntaxChar( b : Byte);
140
141 private
142 // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its corresponding hex value
143 class function HexVal( ch : Byte) : Byte;
144
145 // Convert a byte containing a hex value to its corresponding hex character
146 class function HexChar( val : Byte) : Byte;
147
148 // Write the bytes in array buf as a JSON characters, escaping as needed
149 procedure WriteJSONString( const b : TBytes); overload;
150 procedure WriteJSONString( str : string); overload;
151
152 // Write out number as a JSON value. If the context dictates so, it will be
153 // wrapped in quotes to output as a JSON string.
154 procedure WriteJSONInteger( num : Int64);
155
156 // Write out a double as a JSON value. If it is NaN or infinity or if the
157 // context dictates escaping, Write out as JSON string.
158 procedure WriteJSONDouble( const num : Double);
159
160 // Write out contents of byte array b as a JSON string with base-64 encoded data
161 procedure WriteJSONBase64( const b : TBytes);
162
163 procedure WriteJSONObjectStart;
164 procedure WriteJSONObjectEnd;
165 procedure WriteJSONArrayStart;
166 procedure WriteJSONArrayEnd;
167
168 public
169 // IProtocol
170 procedure WriteMessageBegin( aMsg : IMessage); override;
171 procedure WriteMessageEnd; override;
172 procedure WriteStructBegin(struc: IStruct); override;
173 procedure WriteStructEnd; override;
174 procedure WriteFieldBegin(field: IField); override;
175 procedure WriteFieldEnd; override;
176 procedure WriteFieldStop; override;
177 procedure WriteMapBegin(map: IMap); override;
178 procedure WriteMapEnd; override;
179 procedure WriteListBegin( list: IList); override;
180 procedure WriteListEnd(); override;
181 procedure WriteSetBegin( set_: ISet ); override;
182 procedure WriteSetEnd(); override;
183 procedure WriteBool( b: Boolean); override;
184 procedure WriteByte( b: ShortInt); override;
185 procedure WriteI16( i16: SmallInt); override;
186 procedure WriteI32( i32: Integer); override;
187 procedure WriteI64( i64: Int64); override;
188 procedure WriteDouble( d: Double); override;
189 procedure WriteString( const s: string ); override;
190 procedure WriteBinary( const b: TBytes); override;
191 //
192 function ReadMessageBegin: IMessage; override;
193 procedure ReadMessageEnd(); override;
194 function ReadStructBegin: IStruct; override;
195 procedure ReadStructEnd; override;
196 function ReadFieldBegin: IField; override;
197 procedure ReadFieldEnd(); override;
198 function ReadMapBegin: IMap; override;
199 procedure ReadMapEnd(); override;
200 function ReadListBegin: IList; override;
201 procedure ReadListEnd(); override;
202 function ReadSetBegin: ISet; override;
203 procedure ReadSetEnd(); override;
204 function ReadBool: Boolean; override;
205 function ReadByte: ShortInt; override;
206 function ReadI16: SmallInt; override;
207 function ReadI32: Integer; override;
208 function ReadI64: Int64; override;
209 function ReadDouble:Double; override;
210 function ReadString : string; override;
211 function ReadBinary: TBytes; override;
212
213
214 private
215 // Reading methods.
216
217 // Read in a JSON string, unescaping as appropriate.
218 // Skip Reading from the context if skipContext is true.
219 function ReadJSONString( skipContext : Boolean) : TBytes;
220
221 // Return true if the given byte could be a valid part of a JSON number.
222 function IsJSONNumeric( b : Byte) : Boolean;
223
224 // Read in a sequence of characters that are all valid in JSON numbers. Does
225 // not do a complete regex check to validate that this is actually a number.
226 function ReadJSONNumericChars : String;
227
228 // Read in a JSON number. If the context dictates, Read in enclosing quotes.
229 function ReadJSONInteger : Int64;
230
231 // Read in a JSON double value. Throw if the value is not wrapped in quotes
232 // when expected or if wrapped in quotes when not expected.
233 function ReadJSONDouble : Double;
234
235 // Read in a JSON string containing base-64 encoded data and decode it.
236 function ReadJSONBase64 : TBytes;
237
238 procedure ReadJSONObjectStart;
239 procedure ReadJSONObjectEnd;
240 procedure ReadJSONArrayStart;
241 procedure ReadJSONArrayEnd;
242 end;
243
244
245implementation
246
247var
248 COMMA : TBytes;
249 COLON : TBytes;
250 LBRACE : TBytes;
251 RBRACE : TBytes;
252 LBRACKET : TBytes;
253 RBRACKET : TBytes;
254 QUOTE : TBytes;
255 BACKSLASH : TBytes;
256 ZERO : TBytes;
257 ESCSEQ : TBytes;
258
259const
260 VERSION = 1;
261 JSON_CHAR_TABLE : array[0..$2F] of Byte
262 = (0,0,0,0, 0,0,0,0, Byte('b'),Byte('t'),Byte('n'),0, Byte('f'),Byte('r'),0,0,
263 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
264 1,1,Byte('"'),1, 1,1,1,1, 1,1,1,1, 1,1,1,1);
265
266 ESCAPE_CHARS = '"\btnfr';
267 ESCAPE_CHAR_VALS = '"\'#8#9#10#12#13;
268
269 DEF_STRING_SIZE = 16;
270
271 NAME_BOOL = 'tf';
272 NAME_BYTE = 'i8';
273 NAME_I16 = 'i16';
274 NAME_I32 = 'i32';
275 NAME_I64 = 'i64';
276 NAME_DOUBLE = 'dbl';
277 NAME_STRUCT = 'rec';
278 NAME_STRING = 'str';
279 NAME_MAP = 'map';
280 NAME_LIST = 'lst';
281 NAME_SET = 'set';
282
283 INVARIANT_CULTURE : TFormatSettings
284 = ( ThousandSeparator: ',';
285 DecimalSeparator: '.');
286
287
288
289//--- TJSONProtocolImpl ----------------------
290
291
292function TJSONProtocolImpl.TFactory.GetProtocol( trans: ITransport): IProtocol;
293begin
294 result := TJSONProtocolImpl.Create(trans);
295end;
296
297class function TJSONProtocolImpl.GetTypeNameForTypeID(typeID : TType) : string;
298begin
299 case typeID of
300 TType.Bool_: result := NAME_BOOL;
301 TType.Byte_: result := NAME_BYTE;
302 TType.I16: result := NAME_I16;
303 TType.I32: result := NAME_I32;
304 TType.I64: result := NAME_I64;
305 TType.Double_: result := NAME_DOUBLE;
306 TType.String_: result := NAME_STRING;
307 TType.Struct: result := NAME_STRUCT;
308 TType.Map: result := NAME_MAP;
309 TType.Set_: result := NAME_SET;
310 TType.List: result := NAME_LIST;
311 else
312 raise TProtocolException.Create( TProtocolException.NOT_IMPLEMENTED, 'Unrecognized type ('+IntToStr(Ord(typeID))+')');
313 end;
314end;
315
316
317class function TJSONProtocolImpl.GetTypeIDForTypeName( const name : string) : TType;
318begin
319 if name = NAME_BOOL then result := TType.Bool_
320 else if name = NAME_BYTE then result := TType.Byte_
321 else if name = NAME_I16 then result := TType.I16
322 else if name = NAME_I32 then result := TType.I32
323 else if name = NAME_I64 then result := TType.I64
324 else if name = NAME_DOUBLE then result := TType.Double_
325 else if name = NAME_STRUCT then result := TType.Struct
326 else if name = NAME_STRING then result := TType.String_
327 else if name = NAME_MAP then result := TType.Map
328 else if name = NAME_LIST then result := TType.List
329 else if name = NAME_SET then result := TType.Set_
330 else raise TProtocolException.Create( TProtocolException.NOT_IMPLEMENTED, 'Unrecognized type ('+name+')');
331end;
332
333
334constructor TJSONProtocolImpl.TJSONBaseContext.Create( const aProto : IJSONProtocol);
335begin
336 inherited Create;
337 FProto := aProto;
338end;
339
340
341procedure TJSONProtocolImpl.TJSONBaseContext.Write;
342begin
343 // nothing
344end;
345
346
347procedure TJSONProtocolImpl.TJSONBaseContext.Read;
348begin
349 // nothing
350end;
351
352
353function TJSONProtocolImpl.TJSONBaseContext.EscapeNumbers : Boolean;
354begin
355 result := FALSE;
356end;
357
358
359constructor TJSONProtocolImpl.TJSONListContext.Create( const aProto : IJSONProtocol);
360begin
361 inherited Create( aProto);
362 FFirst := TRUE;
363end;
364
365
366procedure TJSONProtocolImpl.TJSONListContext.Write;
367begin
368 if FFirst
369 then FFirst := FALSE
370 else FProto.Transport.Write( COMMA);
371end;
372
373
374procedure TJSONProtocolImpl.TJSONListContext.Read;
375begin
376 if FFirst
377 then FFirst := FALSE
378 else FProto.ReadJSONSyntaxChar( COMMA[0]);
379end;
380
381
382constructor TJSONProtocolImpl.TJSONPairContext.Create( const aProto : IJSONProtocol);
383begin
384 inherited Create( aProto);
385 FFirst := TRUE;
386 FColon := TRUE;
387end;
388
389
390procedure TJSONProtocolImpl.TJSONPairContext.Write;
391begin
392 if FFirst then begin
393 FFirst := FALSE;
394 FColon := TRUE;
395 end
396 else begin
397 if FColon
398 then FProto.Transport.Write( COLON)
399 else FProto.Transport.Write( COMMA);
400 FColon := not FColon;
401 end;
402end;
403
404
405procedure TJSONProtocolImpl.TJSONPairContext.Read;
406begin
407 if FFirst then begin
408 FFirst := FALSE;
409 FColon := TRUE;
410 end
411 else begin
412 if FColon
413 then FProto.ReadJSONSyntaxChar( COLON[0])
414 else FProto.ReadJSONSyntaxChar( COMMA[0]);
415 FColon := not FColon;
416 end;
417end;
418
419
420function TJSONProtocolImpl.TJSONPairContext.EscapeNumbers : Boolean;
421begin
422 result := FColon;
423end;
424
425
426constructor TJSONProtocolImpl.TLookaheadReader.Create( const aProto : IJSONProtocol);
427begin
428 inherited Create;
429 FProto := aProto;
430 FHasData := FALSE;
431end;
432
433
434function TJSONProtocolImpl.TLookaheadReader.Read : Byte;
435begin
436 if FHasData
437 then FHasData := FALSE
438 else begin
439 SetLength( FData, 1);
440 FProto.Transport.ReadAll( FData, 0, 1);
441 end;
442 result := FData[0];
443end;
444
445
446function TJSONProtocolImpl.TLookaheadReader.Peek : Byte;
447begin
448 if not FHasData then begin
449 SetLength( FData, 1);
450 FProto.Transport.ReadAll( FData, 0, 1);
451 FHasData := TRUE;
452 end;
453 result := FData[0];
454end;
455
456
457procedure TJSONProtocolImpl.PushContext( aCtx : TJSONBaseContext);
458begin
459 FContextStack.Push( FContext);
460 FContext := aCtx;
461end;
462
463procedure TJSONProtocolImpl.PopContext;
464begin
465 FreeAndNil(FContext);
466 FContext := FContextStack.Pop;
467end;
468
469
470constructor TJSONProtocolImpl.Create( aTrans : ITransport);
471begin
472 inherited Create( aTrans);
473
474 // Stack of nested contexts that we may be in
475 FContextStack := TStack<TJSONBaseContext>.Create;
476
477 FContext := TJSONBaseContext.Create( Self);
478 FReader := TLookaheadReader.Create( Self);
479end;
480
481
482destructor TJSONProtocolImpl.Destroy;
483begin
484 try
485 FreeAndNil( FReader);
486 FreeAndNil( FContext);
487 FreeAndNil( FContextStack);
488 finally
489 inherited Destroy;
490 end;
491end;
492
493
494procedure TJSONProtocolImpl.ReadJSONSyntaxChar( b : Byte);
495var ch : Byte;
496begin
497 ch := FReader.Read;
498 if (ch <> b)
499 then raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Unexpected character ('+Char(ch)+')');
500end;
501
502
503class function TJSONProtocolImpl.HexVal( ch : Byte) : Byte;
504var i : Integer;
505begin
506 i := StrToIntDef( '$0'+Char(ch), -1);
507 if (0 <= i) and (i < $10)
508 then result := i
509 else raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Expected hex character ('+Char(ch)+')');
510end;
511
512
513class function TJSONProtocolImpl.HexChar( val : Byte) : Byte;
514const HEXCHARS = '0123456789ABCDEF';
515begin
516 result := Byte( PChar(HEXCHARS)[val and $0F]);
517 ASSERT( Pos( Char(result), HEXCHARS) > 0);
518end;
519
520
521procedure TJSONProtocolImpl.WriteJSONString( str : string);
522begin
523 WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( str));
524end;
525
526
527procedure TJSONProtocolImpl.WriteJSONString( const b : TBytes);
528var i : Integer;
529 tmp : TBytes;
530begin
531 FContext.Write;
532 Transport.Write( QUOTE);
533 for i := 0 to Length(b)-1 do begin
534
535 if (b[i] and $00FF) >= $30 then begin
536
537 if (b[i] = BACKSLASH[0]) then begin
538 Transport.Write( BACKSLASH);
539 Transport.Write( BACKSLASH);
540 end
541 else begin
542 Transport.Write( b, i, 1);
543 end;
544
545 end
546 else begin
547 SetLength( tmp, 2);
548 tmp[0] := JSON_CHAR_TABLE[b[i]];
549 if (tmp[0] = 1) then begin
550 Transport.Write( b, i, 1)
551 end
552 else if (tmp[0] > 1) then begin
553 Transport.Write( BACKSLASH);
554 Transport.Write( tmp, 0, 1);
555 end
556 else begin
557 Transport.Write( ESCSEQ);
558 tmp[0] := HexChar( b[i] div $10);
559 tmp[1] := HexChar( b[i]);
560 Transport.Write( tmp, 0, 2);
561 end;
562 end;
563 end;
564 Transport.Write( QUOTE);
565end;
566
567
568procedure TJSONProtocolImpl.WriteJSONInteger( num : Int64);
569var str : String;
570 escapeNum : Boolean;
571begin
572 FContext.Write;
573 str := IntToStr(num);
574
575 escapeNum := FContext.EscapeNumbers;
576 if escapeNum
577 then Transport.Write( QUOTE);
578
579 Transport.Write( SysUtils.TEncoding.UTF8.GetBytes( str));
580
581 if escapeNum
582 then Transport.Write( QUOTE);
583end;
584
585
586procedure TJSONProtocolImpl.WriteJSONDouble( const num : Double);
587var str : string;
588 special : Boolean;
589 escapeNum : Boolean;
590begin
591 FContext.Write;
592
593 str := FloatToStr( num, INVARIANT_CULTURE);
594 special := FALSE;
595
596 case UpCase(str[1]) of
597 'N' : special := TRUE; // NaN
598 'I' : special := TRUE; // Infinity
599 '-' : special := (UpCase(str[2]) = 'I'); // -Infinity
600 end;
601
602 escapeNum := special or FContext.EscapeNumbers;
603
604
605 if escapeNum
606 then Transport.Write( QUOTE);
607
608 Transport.Write( SysUtils.TEncoding.UTF8.GetBytes( str));
609
610 if escapeNum
611 then Transport.Write( QUOTE);
612end;
613
614
615procedure TJSONProtocolImpl.WriteJSONBase64( const b : TBytes);
616var str : string;
617 tmp : TBytes;
618 i : Integer;
619begin
620 FContext.Write;
621 Transport.Write( QUOTE);
622
623 // First base64-encode b, then write the resulting 8-bit chars
624 // Unfortunately, EncodeBytes() returns a string of 16-bit (wide) chars
625 // And for the sake of efficiency, we want to write everything at once
626 str := TIdEncoderMIME.EncodeBytes(b);
627 ASSERT( SizeOf(str[1]) = SizeOf(Word));
628 SetLength( tmp, Length(str));
629 for i := 1 to Length(str) do begin
630 ASSERT( Hi(Word(str[i])) = 0); // base64 consists of a well-defined set of 8-bit chars only
631 tmp[i-1] := Lo(Word(str[i])); // extract the lower byte
632 end;
633 Transport.Write( tmp); // now write all the data
634
635 Transport.Write( QUOTE);
636end;
637
638
639procedure TJSONProtocolImpl.WriteJSONObjectStart;
640begin
641 FContext.Write;
642 Transport.Write( LBRACE);
643 PushContext( TJSONPairContext.Create( Self));
644end;
645
646
647procedure TJSONProtocolImpl.WriteJSONObjectEnd;
648begin
649 PopContext;
650 Transport.Write( RBRACE);
651end;
652
653
654procedure TJSONProtocolImpl.WriteJSONArrayStart;
655begin
656 FContext.Write;
657 Transport.Write( LBRACKET);
658 PushContext( TJSONListContext.Create( Self));
659end;
660
661
662procedure TJSONProtocolImpl.WriteJSONArrayEnd;
663begin
664 PopContext;
665 Transport.Write( RBRACKET);
666end;
667
668
669procedure TJSONProtocolImpl.WriteMessageBegin( aMsg : IMessage);
670begin
671 WriteJSONArrayStart;
672 WriteJSONInteger(VERSION);
673
674 WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( aMsg.Name));
675
676 WriteJSONInteger( LongInt( aMsg.Type_));
677 WriteJSONInteger( aMsg.SeqID);
678end;
679
680procedure TJSONProtocolImpl.WriteMessageEnd;
681begin
682 WriteJSONArrayEnd;
683end;
684
685
686procedure TJSONProtocolImpl.WriteStructBegin( struc: IStruct);
687begin
688 WriteJSONObjectStart;
689end;
690
691
692procedure TJSONProtocolImpl.WriteStructEnd;
693begin
694 WriteJSONObjectEnd;
695end;
696
697
698procedure TJSONProtocolImpl.WriteFieldBegin( field : IField);
699begin
700 WriteJSONInteger(field.ID);
701 WriteJSONObjectStart;
702 WriteJSONString( GetTypeNameForTypeID(field.Type_));
703end;
704
705
706procedure TJSONProtocolImpl.WriteFieldEnd;
707begin
708 WriteJSONObjectEnd;
709end;
710
711
712procedure TJSONProtocolImpl.WriteFieldStop;
713begin
714 // nothing to do
715end;
716
717procedure TJSONProtocolImpl.WriteMapBegin( map: IMap);
718begin
719 WriteJSONArrayStart;
720 WriteJSONString( GetTypeNameForTypeID( map.KeyType));
721 WriteJSONString( GetTypeNameForTypeID( map.ValueType));
722 WriteJSONInteger( map.Count);
723 WriteJSONObjectStart;
724end;
725
726
727procedure TJSONProtocolImpl.WriteMapEnd;
728begin
729 WriteJSONObjectEnd;
730 WriteJSONArrayEnd;
731end;
732
733
734procedure TJSONProtocolImpl.WriteListBegin( list: IList);
735begin
736 WriteJSONArrayStart;
737 WriteJSONString( GetTypeNameForTypeID( list.ElementType));
738 WriteJSONInteger(list.Count);
739end;
740
741
742procedure TJSONProtocolImpl.WriteListEnd;
743begin
744 WriteJSONArrayEnd;
745end;
746
747
748procedure TJSONProtocolImpl.WriteSetBegin( set_: ISet);
749begin
750 WriteJSONArrayStart;
751 WriteJSONString( GetTypeNameForTypeID( set_.ElementType));
752 WriteJSONInteger( set_.Count);
753end;
754
755
756procedure TJSONProtocolImpl.WriteSetEnd;
757begin
758 WriteJSONArrayEnd;
759end;
760
761procedure TJSONProtocolImpl.WriteBool( b: Boolean);
762begin
763 if b
764 then WriteJSONInteger( 1)
765 else WriteJSONInteger( 0);
766end;
767
768procedure TJSONProtocolImpl.WriteByte( b: ShortInt);
769begin
770 WriteJSONInteger( b);
771end;
772
773procedure TJSONProtocolImpl.WriteI16( i16: SmallInt);
774begin
775 WriteJSONInteger( i16);
776end;
777
778procedure TJSONProtocolImpl.WriteI32( i32: Integer);
779begin
780 WriteJSONInteger( i32);
781end;
782
783procedure TJSONProtocolImpl.WriteI64( i64: Int64);
784begin
785 WriteJSONInteger(i64);
786end;
787
788procedure TJSONProtocolImpl.WriteDouble( d: Double);
789begin
790 WriteJSONDouble( d);
791end;
792
793procedure TJSONProtocolImpl.WriteString( const s: string );
794begin
795 WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( s));
796end;
797
798procedure TJSONProtocolImpl.WriteBinary( const b: TBytes);
799begin
800 WriteJSONBase64( b);
801end;
802
803
804function TJSONProtocolImpl.ReadJSONString( skipContext : Boolean) : TBytes;
805var buffer : TMemoryStream;
806 ch : Byte;
807 off : Integer;
808 tmp : TBytes;
809begin
810 buffer := TMemoryStream.Create;
811 try
812 if not skipContext
813 then FContext.Read;
814
815 ReadJSONSyntaxChar( QUOTE[0]);
816
817 while TRUE do begin
818 ch := FReader.Read;
819
820 if (ch = QUOTE[0])
821 then Break;
822
823 if (ch = ESCSEQ[0])
824 then begin
825 ch := FReader.Read;
826 if (ch = ESCSEQ[1])
827 then begin
828 ReadJSONSyntaxChar( ZERO[0]);
829 ReadJSONSyntaxChar( ZERO[0]);
830 SetLength( tmp, 2);
831 Transport.ReadAll( tmp, 0, 2);
832 ch := (HexVal(tmp[0]) shl 4) + HexVal(tmp[1]);
833 end
834 else begin
835 off := Pos( Char(ch), ESCAPE_CHARS);
836 if off < 1
837 then raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Expected control char');
838 ch := Byte( ESCAPE_CHAR_VALS[off]);
839 end;
840 end;
841 buffer.Write( ch, 1);
842 end;
843
844 SetLength( result, buffer.Size);
Jake Farrella2a9ee92011-12-15 20:50:31 +0000845 if buffer.Size > 0 then Move( buffer.Memory^, result[0], Length(result));
Jake Farrell27274222011-11-10 20:32:44 +0000846
847 finally
848 buffer.Free;
849 end;
850end;
851
852
853function TJSONProtocolImpl.IsJSONNumeric( b : Byte) : Boolean;
854const NUMCHARS = ['+','-','.','0','1','2','3','4','5','6','7','8','9','E','e'];
855begin
856 result := CharInSet( Char(b), NUMCHARS);
857end;
858
859
860function TJSONProtocolImpl.ReadJSONNumericChars : string;
861var strbld : TThriftStringBuilder;
862 ch : Byte;
863begin
864 strbld := TThriftStringBuilder.Create;
865 try
866 while TRUE do begin
867 ch := FReader.Peek;
868 if IsJSONNumeric(ch)
869 then strbld.Append( Char(FReader.Read))
870 else Break;
871 end;
872 result := strbld.ToString;
873
874 finally
875 strbld.Free;
876 end;
877end;
878
879
880function TJSONProtocolImpl.ReadJSONInteger : Int64;
881var str : string;
882begin
883 FContext.Read;
884 if FContext.EscapeNumbers
885 then ReadJSONSyntaxChar( QUOTE[0]);
886
887 str := ReadJSONNumericChars;
888
889 if FContext.EscapeNumbers
890 then ReadJSONSyntaxChar( QUOTE[0]);
891
892 try
893 result := StrToInt64(str);
894 except
895 on e:Exception do begin
896 raise TProtocolException.Create( TProtocolException.INVALID_DATA,
897 'Bad data encounted in numeric data ('+str+') ('+e.Message+')');
898 end;
899 end;
900end;
901
902
903function TJSONProtocolImpl.ReadJSONDouble : Double;
904var dub : Double;
905 str : string;
906begin
907 FContext.Read;
908
909 if FReader.Peek = QUOTE[0]
910 then begin
911 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( TRUE));
912 dub := StrToFloat( str, INVARIANT_CULTURE);
913
914 if not FContext.EscapeNumbers()
915 and not Math.IsNaN(dub)
916 and not Math.IsInfinite(dub)
917 then begin
918 // Throw exception -- we should not be in a string in Self case
919 raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Numeric data unexpectedly quoted');
920 end;
921 result := dub;
922 Exit;
923 end;
924
925 // will throw - we should have had a quote if escapeNum == true
926 if FContext.EscapeNumbers
927 then ReadJSONSyntaxChar( QUOTE[0]);
928
929 try
930 str := ReadJSONNumericChars;
931 result := StrToFloat( str, INVARIANT_CULTURE);
932 except
933 on e:Exception
934 do raise TProtocolException.Create( TProtocolException.INVALID_DATA,
935 'Bad data encounted in numeric data ('+str+') ('+e.Message+')');
936 end;
937end;
938
939
940function TJSONProtocolImpl.ReadJSONBase64 : TBytes;
941var b : TBytes;
942 str : string;
943begin
944 b := ReadJSONString(false);
945
946 SetString( str, PAnsiChar(b), Length(b));
947 result := TIdDecoderMIME.DecodeBytes( str);
948end;
949
950
951procedure TJSONProtocolImpl.ReadJSONObjectStart;
952begin
953 FContext.Read;
954 ReadJSONSyntaxChar( LBRACE[0]);
955 PushContext( TJSONPairContext.Create( Self));
956end;
957
958
959procedure TJSONProtocolImpl.ReadJSONObjectEnd;
960begin
961 ReadJSONSyntaxChar( RBRACE[0]);
962 PopContext;
963end;
964
965
966procedure TJSONProtocolImpl.ReadJSONArrayStart;
967begin
968 FContext.Read;
969 ReadJSONSyntaxChar( LBRACKET[0]);
970 PushContext( TJSONListContext.Create( Self));
971end;
972
973
974procedure TJSONProtocolImpl.ReadJSONArrayEnd;
975begin
976 ReadJSONSyntaxChar( RBRACKET[0]);
977 PopContext;
978end;
979
980
981function TJSONProtocolImpl.ReadMessageBegin: IMessage;
982begin
983 result := TMessageImpl.Create;
984 ReadJSONArrayStart;
985
986 if ReadJSONInteger <> VERSION
987 then raise TProtocolException.Create( TProtocolException.BAD_VERSION, 'Message contained bad version.');
988
989 result.Name := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
990 result.Type_ := TMessageType( ReadJSONInteger);
991 result.SeqID := ReadJSONInteger;
992end;
993
994
995procedure TJSONProtocolImpl.ReadMessageEnd;
996begin
997 ReadJSONArrayEnd;
998end;
999
1000
1001function TJSONProtocolImpl.ReadStructBegin : IStruct ;
1002begin
1003 ReadJSONObjectStart;
1004 result := TStructImpl.Create('');
1005end;
1006
1007
1008procedure TJSONProtocolImpl.ReadStructEnd;
1009begin
1010 ReadJSONObjectEnd;
1011end;
1012
1013
1014function TJSONProtocolImpl.ReadFieldBegin : IField;
1015var ch : Byte;
1016 str : string;
1017begin
1018 result := TFieldImpl.Create;
1019 ch := FReader.Peek;
1020 if ch = RBRACE[0]
1021 then result.Type_ := TType.Stop
1022 else begin
1023 result.ID := ReadJSONInteger;
1024 ReadJSONObjectStart;
1025
1026 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
1027 result.Type_ := GetTypeIDForTypeName( str);
1028 end;
1029end;
1030
1031
1032procedure TJSONProtocolImpl.ReadFieldEnd;
1033begin
1034 ReadJSONObjectEnd;
1035end;
1036
1037
1038function TJSONProtocolImpl.ReadMapBegin : IMap;
1039var str : string;
1040begin
1041 result := TMapImpl.Create;
1042 ReadJSONArrayStart;
1043
1044 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1045 result.KeyType := GetTypeIDForTypeName( str);
1046
1047 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1048 result.ValueType := GetTypeIDForTypeName( str);
1049
1050 result.Count := ReadJSONInteger;
1051 ReadJSONObjectStart;
1052end;
1053
1054
1055procedure TJSONProtocolImpl.ReadMapEnd;
1056begin
1057 ReadJSONObjectEnd;
1058 ReadJSONArrayEnd;
1059end;
1060
1061
1062function TJSONProtocolImpl.ReadListBegin : IList;
1063var str : string;
1064begin
1065 result := TListImpl.Create;
1066 ReadJSONArrayStart;
1067
1068 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1069 result.ElementType := GetTypeIDForTypeName( str);
1070 result.Count := ReadJSONInteger;
1071end;
1072
1073
1074procedure TJSONProtocolImpl.ReadListEnd;
1075begin
1076 ReadJSONArrayEnd;
1077end;
1078
1079
1080function TJSONProtocolImpl.ReadSetBegin : ISet;
1081var str : string;
1082begin
1083 result := TSetImpl.Create;
1084 ReadJSONArrayStart;
1085
1086 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1087 result.ElementType := GetTypeIDForTypeName( str);
1088 result.Count := ReadJSONInteger;
1089end;
1090
1091
1092procedure TJSONProtocolImpl.ReadSetEnd;
1093begin
1094 ReadJSONArrayEnd;
1095end;
1096
1097
1098function TJSONProtocolImpl.ReadBool : Boolean;
1099begin
1100 result := (ReadJSONInteger <> 0);
1101end;
1102
1103
1104function TJSONProtocolImpl.ReadByte : ShortInt;
1105begin
1106 result := ReadJSONInteger;
1107end;
1108
1109
1110function TJSONProtocolImpl.ReadI16 : SmallInt;
1111begin
1112 result := ReadJSONInteger;
1113end;
1114
1115
1116function TJSONProtocolImpl.ReadI32 : LongInt;
1117begin
1118 result := ReadJSONInteger;
1119end;
1120
1121
1122function TJSONProtocolImpl.ReadI64 : Int64;
1123begin
1124 result := ReadJSONInteger;
1125end;
1126
1127
1128function TJSONProtocolImpl.ReadDouble : Double;
1129begin
1130 result := ReadJSONDouble;
1131end;
1132
1133
1134function TJSONProtocolImpl.ReadString : string;
1135begin
1136 result := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
1137end;
1138
1139
1140function TJSONProtocolImpl.ReadBinary : TBytes;
1141begin
1142 result := ReadJSONBase64;
1143end;
1144
1145
1146//--- init code ---
1147
1148procedure InitBytes( var b : TBytes; aData : array of Byte);
1149begin
1150 SetLength( b, Length(aData));
1151 Move( aData, b[0], Length(b));
1152end;
1153
1154initialization
1155 InitBytes( COMMA, [Byte(',')]);
1156 InitBytes( COLON, [Byte(':')]);
1157 InitBytes( LBRACE, [Byte('{')]);
1158 InitBytes( RBRACE, [Byte('}')]);
1159 InitBytes( LBRACKET, [Byte('[')]);
1160 InitBytes( RBRACKET, [Byte(']')]);
1161 InitBytes( QUOTE, [Byte('"')]);
1162 InitBytes( BACKSLASH, [Byte('\')]);
1163 InitBytes( ZERO, [Byte('0')]);
1164 InitBytes( ESCSEQ, [Byte('\'),Byte('u'),Byte('0'),Byte('0')]);
1165end.