123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976 |
- (* *****************************************************************************
- * PasJSON *
- ******************************************************************************
- * Version 2020-03-04-02-20 *
- ******************************************************************************
- * zlib license *
- *============================================================================*
- * *
- * Copyright (C) 2016-2020, Benjamin Rosseaux ([email protected]) *
- * *
- * This software is provided 'as-is', without any express or implied *
- * warranty. In no event will the authors be held liable for any damages *
- * arising from the use of this software. *
- * *
- * Permission is granted to anyone to use this software for any purpose, *
- * including commercial applications, and to alter it and redistribute it *
- * freely, subject to the following restrictions: *
- * *
- * 1. The origin of this software must not be misrepresented; you must not *
- * claim that you wrote the original software. If you use this software *
- * in a product, an acknowledgement in the product documentation would be *
- * appreciated but is not required. *
- * 2. Altered source versions must be plainly marked as such, and must not be *
- * misrepresented as being the original software. *
- * 3. This notice may not be removed or altered from any source distribution. *
- * *
- ******************************************************************************
- * General guidelines for code contributors *
- *============================================================================*
- * *
- * 1. Make sure you are legally allowed to make a contribution under the zlib *
- * license. *
- * 2. The zlib license header goes at the top of each source file, with *
- * appropriate copyright notice. *
- * 3. After a pull request, check the status of your pull request on *
- * http://github.com/BeRo1985/pasjson *
- * 4. Write code which's compatible with newer modern Delphi versions and *
- * FreePascal >= 3.0.0 *
- * 5. Don't use Delphi-only, FreePascal-only or Lazarus-only libraries/units, *
- * but if needed, make it out-ifdef-able. *
- * 6. No use of third-party libraries/units as possible, but if needed, make *
- * it out-ifdef-able. *
- * 7. Try to use const when possible. *
- * 8. Make sure to comment out writeln, used while debugging. *
- * 9. Make sure the code compiles on 32-bit and 64-bit platforms (x86-32, *
- * x86-64, ARM, ARM64, etc.). *
- * *
- ***************************************************************************** *)
- unit PasJSON;
- {$ifdef fpc}
- {$mode delphi}
- {$ifdef cpui386}
- {$define cpu386}
- {$endif}
- {$ifdef cpu386}
- {$asmmode intel}
- {$endif}
- {$ifdef cpuamd64}
- {$asmmode intel}
- {$endif}
- {$ifdef FPC_LITTLE_ENDIAN}
- {$define LITTLE_ENDIAN}
- {$else}
- {$ifdef FPC_BIG_ENDIAN}
- {$define BIG_ENDIAN}
- {$endif}
- {$endif}
- { -$pic off }
- {$ifdef fpc_has_internal_sar}
- {$define HasSAR}
- {$endif}
- {$ifdef FPC_HAS_TYPE_EXTENDED}
- {$define HAS_TYPE_EXTENDED}
- {$else}
- {$undef HAS_TYPE_EXTENDED}
- {$endif}
- {$ifdef FPC_HAS_TYPE_DOUBLE}
- {$define HAS_TYPE_DOUBLE}
- {$else}
- {$undef HAS_TYPE_DOUBLE}
- {$endif}
- {$ifdef FPC_HAS_TYPE_SINGLE}
- {$define HAS_TYPE_SINGLE}
- {$else}
- {$undef HAS_TYPE_SINGLE}
- {$endif}
- {$define CAN_INLINE}
- {$define HAS_ADVANCED_RECORDS}
- {$else}
- {$realcompatibility off}
- {$localsymbols on}
- {$define LITTLE_ENDIAN}
- {$ifndef cpu64}
- {$define cpu32}
- {$endif}
- {$define HAS_TYPE_EXTENDED}
- {$define HAS_TYPE_DOUBLE}
- {$define HAS_TYPE_SINGLE}
- {$undef CAN_INLINE}
- {$undef HAS_ADVANCED_RECORDS}
- {$ifndef BCB}
- {$ifdef ver120}
- {$define Delphi4or5}
- {$endif}
- {$ifdef ver130}
- {$define Delphi4or5}
- {$endif}
- {$ifdef ver140}
- {$define Delphi6}
- {$endif}
- {$ifdef ver150}
- {$define Delphi7}
- {$endif}
- {$ifdef ver170}
- {$define Delphi2005}
- {$endif}
- {$else}
- {$ifdef ver120}
- {$define Delphi4or5}
- {$define BCB4}
- {$endif}
- {$ifdef ver130}
- {$define Delphi4or5}
- {$endif}
- {$endif}
- {$ifdef conditionalexpressions}
- {$if CompilerVersion>=24.0}
- {$legacyifend on}
- {$ifend}
- {$if CompilerVersion>=14.0}
- {$if CompilerVersion=14.0}
- {$define Delphi6}
- {$ifend}
- {$define Delphi6AndUp}
- {$ifend}
- {$if CompilerVersion>=15.0}
- {$if CompilerVersion=15.0}
- {$define Delphi7}
- {$ifend}
- {$define Delphi7AndUp}
- {$ifend}
- {$if CompilerVersion>=17.0}
- {$if CompilerVersion=17.0}
- {$define Delphi2005}
- {$ifend}
- {$define Delphi2005AndUp}
- {$ifend}
- {$if CompilerVersion>=18.0}
- {$if CompilerVersion=18.0}
- {$define BDS2006}
- {$define Delphi2006}
- {$ifend}
- {$define Delphi2006AndUp}
- {$define CAN_INLINE}
- {$define HAS_ADVANCED_RECORDS}
- {$ifend}
- {$if CompilerVersion>=18.5}
- {$if CompilerVersion=18.5}
- {$define Delphi2007}
- {$ifend}
- {$define Delphi2007AndUp}
- {$ifend}
- {$if CompilerVersion=19.0}
- {$define Delphi2007Net}
- {$ifend}
- {$if CompilerVersion>=20.0}
- {$if CompilerVersion=20.0}
- {$define Delphi2009}
- {$ifend}
- {$define Delphi2009AndUp}
- {$ifend}
- {$if CompilerVersion>=21.0}
- {$if CompilerVersion=21.0}
- {$define Delphi2010}
- {$ifend}
- {$define Delphi2010AndUp}
- {$ifend}
- {$if CompilerVersion>=22.0}
- {$if CompilerVersion=22.0}
- {$define DelphiXE}
- {$ifend}
- {$define DelphiXEAndUp}
- {$ifend}
- {$if CompilerVersion>=23.0}
- {$if CompilerVersion=23.0}
- {$define DelphiXE2}
- {$ifend}
- {$define DelphiXE2AndUp}
- {$ifend}
- {$if CompilerVersion>=24.0}
- {$legacyifend on}
- {$if CompilerVersion=24.0}
- {$define DelphiXE3}
- {$ifend}
- {$define DelphiXE3AndUp}
- {$ifend}
- {$if CompilerVersion>=25.0}
- {$if CompilerVersion=25.0}
- {$define DelphiXE4}
- {$ifend}
- {$define DelphiXE4AndUp}
- {$ifend}
- {$if CompilerVersion>=26.0}
- {$if CompilerVersion=26.0}
- {$define DelphiXE5}
- {$ifend}
- {$define DelphiXE5AndUp}
- {$ifend}
- {$if CompilerVersion>=27.0}
- {$if CompilerVersion=27.0}
- {$define DelphiXE6}
- {$ifend}
- {$define DelphiXE6AndUp}
- {$ifend}
- {$if CompilerVersion>=28.0}
- {$if CompilerVersion=28.0}
- {$define DelphiXE7}
- {$ifend}
- {$define DelphiXE7AndUp}
- {$ifend}
- {$if CompilerVersion>=29.0}
- {$if CompilerVersion=29.0}
- {$define DelphiXE8}
- {$ifend}
- {$define DelphiXE8AndUp}
- {$ifend}
- {$if CompilerVersion>=30.0}
- {$if CompilerVersion=30.0}
- {$define Delphi10Seattle}
- {$ifend}
- {$define Delphi10SeattleAndUp}
- {$ifend}
- {$if CompilerVersion>=31.0}
- {$if CompilerVersion=31.0}
- {$define Delphi10Berlin}
- {$ifend}
- {$define Delphi10BerlinAndUp}
- {$ifend}
- {$endif}
- {$ifndef Delphi4or5}
- {$ifndef BCB}
- {$define Delphi6AndUp}
- {$endif}
- {$ifndef Delphi6}
- {$define BCB6OrDelphi7AndUp}
- {$ifndef BCB}
- {$define Delphi7AndUp}
- {$endif}
- {$ifndef BCB}
- {$ifndef Delphi7}
- {$ifndef Delphi2005}
- {$define BDS2006AndUp}
- {$endif}
- {$endif}
- {$endif}
- {$endif}
- {$endif}
- {$ifdef Delphi6AndUp}
- {$warn symbol_platform off}
- {$warn symbol_deprecated off}
- {$endif}
- {$endif}
- {$if defined(Win32) or defined(Win64)}
- {$define Windows}
- {$ifend}
- {$rangechecks off}
- {$extendedsyntax on}
- {$writeableconst on}
- {$hints off}
- {$booleval off}
- {$typedaddress off}
- {$stackframes off}
- {$varstringchecks on}
- {$typeinfo on}
- {$overflowchecks off}
- {$longstrings on}
- {$openstrings on}
- {$ifndef HAS_TYPE_SINGLE}
- {$error No single floating point precision}
- {$endif}
- {$ifndef HAS_TYPE_DOUBLE}
- {$error No double floating point precision}
- {$endif}
- {$scopedenums on}
- {$ifndef fpc}
- {$ifdef conditionalexpressions}
- {$if CompilerVersion>=24.0}
- {$legacyifend on}
- {$ifend}
- {$endif}
- {$endif}
- interface
- uses
- System.SysUtils,
- System.Classes,
- System.Math;
- type
- PPPasJSONInt8 = ^PPasJSONInt8;
- PPasJSONInt8 = ^TPasJSONInt8;
- TPasJSONInt8 = {$IFDEF fpc}Int8{$ELSE}shortint{$ENDIF};
- PPPasJSONUInt8 = ^PPasJSONUInt8;
- PPasJSONUInt8 = ^TPasJSONUInt8;
- TPasJSONUInt8 = {$IFDEF fpc}UInt8{$ELSE}byte{$ENDIF};
- PPPasJSONUInt8Array = ^PPasJSONUInt8Array;
- PPasJSONUInt8Array = ^TPasJSONUInt8Array;
- TPasJSONUInt8Array = array [0 .. 65535] of TPasJSONUInt8;
- PPPasJSONInt16 = ^PPasJSONInt16;
- PPasJSONInt16 = ^TPasJSONInt16;
- TPasJSONInt16 = {$IFDEF fpc}Int16{$ELSE}smallint{$ENDIF};
- PPPasJSONUInt16 = ^PPasJSONUInt16;
- PPasJSONUInt16 = ^TPasJSONUInt16;
- TPasJSONUInt16 = {$IFDEF fpc}UInt16{$ELSE}word{$ENDIF};
- PPPasJSONInt32 = ^PPasJSONInt32;
- PPasJSONInt32 = ^TPasJSONInt32;
- TPasJSONInt32 = {$IFDEF fpc}Int32{$ELSE}longint{$ENDIF};
- PPPasJSONUInt32 = ^PPasJSONUInt32;
- PPasJSONUInt32 = ^TPasJSONUInt32;
- TPasJSONUInt32 = {$IFDEF fpc}UInt32{$ELSE}longword{$ENDIF};
- PPPasJSONInt64 = ^PPasJSONInt64;
- PPasJSONInt64 = ^TPasJSONInt64;
- TPasJSONInt64 = Int64;
- PPPasJSONUInt64 = ^PPasJSONUInt64;
- PPasJSONUInt64 = ^TPasJSONUInt64;
- TPasJSONUInt64 = UInt64;
- PPPasJSONChar = ^PAnsiChar;
- PPasJSONChar = PAnsiChar;
- TPasJSONChar = AnsiChar;
- PPPasJSONRawByteChar = ^PAnsiChar;
- PPasJSONRawByteChar = PAnsiChar;
- TPasJSONRawByteChar = AnsiChar;
- PPPasJSONUTF16Char = ^PWideChar;
- PPasJSONUTF16Char = PWideChar;
- TPasJSONUTF16Char = WideChar;
- PPPasJSONPointer = ^PPasJSONPointer;
- PPasJSONPointer = ^TPasJSONPointer;
- TPasJSONPointer = Pointer;
- PPPasJSONPointers = ^PPasJSONPointers;
- PPasJSONPointers = ^TPasJSONPointers;
- TPasJSONPointers = array [0 .. 65535] of TPasJSONPointer;
- PPPasJSONVoid = ^PPasJSONVoid;
- PPasJSONVoid = TPasJSONPointer;
- PPPasJSONFloat = ^PPasJSONFloat;
- PPasJSONFloat = ^TPasJSONFloat;
- TPasJSONFloat = Single;
- TPasJSONFloats = array of TPasJSONFloat;
- PPPasJSONDouble = ^PPasJSONDouble;
- PPasJSONDouble = ^TPasJSONDouble;
- TPasJSONDouble = Double;
- PPPasJSONPtrUInt = ^PPasJSONPtrUInt;
- PPPasJSONPtrInt = ^PPasJSONPtrInt;
- PPasJSONPtrUInt = ^TPasJSONPtrUInt;
- PPasJSONPtrInt = ^TPasJSONPtrInt;
- {$IFDEF fpc}
- TPasJSONPtrUInt = PtrUInt;
- TPasJSONPtrInt = PtrInt;
- {$UNDEF OldDelphi}
- {$ELSE}
- {$IFDEF conditionalexpressions}
- {$IF CompilerVersion>=23.0}
- {$UNDEF OldDelphi}
- TPasJSONPtrUInt = NativeUInt;
- TPasJSONPtrInt = NativeInt;
- {$ELSE}
- {$DEFINE OldDelphi}
- {$IFEND}
- {$ELSE}
- {$DEFINE OldDelphi}
- {$ENDIF}
- {$ENDIF}
- {$IFDEF OldDelphi}
- {$IFDEF cpu64}
- TPasJSONPtrUInt = UInt64;
- TPasJSONPtrInt = Int64;
- {$ELSE}
- TPasJSONPtrUInt = longword;
- TPasJSONPtrInt = longint;
- {$ENDIF}
- {$ENDIF}
- PPPasJSONSizeUInt = ^PPasJSONSizeUInt;
- PPasJSONSizeUInt = ^TPasJSONSizeUInt;
- TPasJSONSizeUInt = TPasJSONPtrUInt;
- PPPasJSONSizeInt = ^PPasJSONSizeInt;
- PPasJSONSizeInt = ^TPasJSONSizeInt;
- TPasJSONSizeInt = TPasJSONPtrInt;
- PPPasJSONNativeUInt = ^PPasJSONNativeUInt;
- PPasJSONNativeUInt = ^TPasJSONNativeUInt;
- TPasJSONNativeUInt = TPasJSONPtrUInt;
- PPPasJSONNativeInt = ^PPasJSONNativeInt;
- PPasJSONNativeInt = ^TPasJSONNativeInt;
- TPasJSONNativeInt = TPasJSONPtrInt;
- PPPasJSONSize = ^PPasJSONSizeUInt;
- PPasJSONSize = ^TPasJSONSizeUInt;
- TPasJSONSize = TPasJSONPtrUInt;
- PPPasJSONPtrDiff = ^PPasJSONPtrDiff;
- PPasJSONPtrDiff = ^TPasJSONPtrDiff;
- TPasJSONPtrDiff = TPasJSONPtrInt;
- PPPasJSONRawByteString = ^PPasJSONRawByteString;
- PPasJSONRawByteString = ^TPasJSONRawByteString;
- TPasJSONRawByteString =
- {$IF declared(RawByteString)}RawByteString{$ELSE}AnsiString{$IFEND};
- PPPasJSONUTF8String = ^PPasJSONUTF8String;
- PPasJSONUTF8String = ^TPasJSONUTF8String;
- TPasJSONUTF8String =
- {$IF declared(UTF8String)}UTF8String{$ELSE}AnsiString{$IFEND};
- PPPasJSONUTF16String = ^PPasJSONUTF16String;
- PPasJSONUTF16String = ^TPasJSONUTF16String;
- TPasJSONUTF16String =
- {$IF declared(UnicodeString)}UnicodeString{$ELSE}WideString{$IFEND};
- EPasJSONSyntaxError = class(Exception)
- private
- fPosition: TPasJSONSizeInt;
- public
- constructor Create(const aMessage: string;
- const aPosition: TPasJSONSizeInt); reintroduce;
- published
- property Position: TPasJSONSizeInt read fPosition write fPosition;
- end;
- EPasJSONMergeError = class(Exception);
- TPasJSONMergeFlag = (ForceObjectPropertyValueDestinationType);
- TPasJSONMergeFlags = set of TPasJSONMergeFlag;
- TPasJSONItem = class
- public
- constructor Create;
- destructor Destroy; override;
- procedure Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []); virtual;
- end;
- TPasJSONItems = array of TPasJSONItem;
- TPasJSONItemNull = class(TPasJSONItem)
- public
- constructor Create;
- destructor Destroy; override;
- procedure Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []); override;
- end;
- TPasJSONItemBoolean = class(TPasJSONItem)
- private
- fValue: boolean;
- public
- constructor Create(const AValue: boolean);
- destructor Destroy; override;
- procedure Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []); override;
- published
- property Value: boolean read fValue write fValue;
- end;
- TPasJSONItemNumber = class(TPasJSONItem)
- private
- fValue: TPasJSONDouble;
- public
- constructor Create(const AValue: TPasJSONDouble);
- destructor Destroy; override;
- procedure Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []); override;
- published
- property Value: TPasJSONDouble read fValue write fValue;
- end;
- TPasJSONItemString = class(TPasJSONItem)
- private
- fValue: TPasJSONUTF8String;
- public
- constructor Create(const AValue: TPasJSONUTF8String);
- destructor Destroy; override;
- procedure Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []); override;
- published
- property Value: TPasJSONUTF8String read fValue write fValue;
- end;
- TPasJSONItemObjectProperty = class
- private
- fKey: TPasJSONUTF8String;
- fValue: TPasJSONItem;
- public
- constructor Create; reintroduce;
- destructor Destroy; override;
- published
- property Key: TPasJSONUTF8String read fKey write fKey;
- property Value: TPasJSONItem read fValue write fValue;
- end;
- TPasJSONItemObjectProperties = array of TPasJSONItemObjectProperty;
- TPasJSONItemObject = class(TPasJSONItem)
- public type
- PPasJSONItemObjectEnumerator = ^TPasJSONItemObjectEnumerator;
- TPasJSONItemObjectEnumerator = record
- private
- fOwner: TPasJSONItemObject;
- fIndex: TPasJSONSizeInt;
- function GetCurrent: TPasJSONItemObjectProperty; inline;
- public
- constructor Create(const aOwner: TPasJSONItemObject);
- function MoveNext: boolean; inline;
- property Current: TPasJSONItemObjectProperty read GetCurrent;
- end;
- private
- fProperties: TPasJSONItemObjectProperties;
- fCount: TPasJSONSizeInt;
- function GetKeyIndex(const aKey: TPasJSONUTF8String): TPasJSONInt32;
- function GetKey(const aIndex: TPasJSONSizeInt): TPasJSONUTF8String;
- procedure SetKey(const aIndex: TPasJSONSizeInt;
- const aKey: TPasJSONUTF8String);
- function GetValue(const aIndex: TPasJSONSizeInt): TPasJSONItem;
- procedure SetValue(const aIndex: TPasJSONSizeInt;
- const aItem: TPasJSONItem);
- function GetProperty(const aKey: TPasJSONUTF8String): TPasJSONItem;
- procedure SetProperty(const aKey: TPasJSONUTF8String;
- const aItem: TPasJSONItem);
- public
- constructor Create;
- destructor Destroy; override;
- function GetEnumerator: TPasJSONItemObjectEnumerator; inline;
- procedure Clear;
- procedure Add(const aKey: TPasJSONUTF8String; const AValue: TPasJSONItem);
- procedure Delete(const aIndex: TPasJSONSizeInt); overload;
- procedure Delete(const aKey: TPasJSONUTF8String); overload;
- procedure Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []); override;
- property Count: TPasJSONSizeInt read fCount;
- property Indices[const Key: TPasJSONUTF8String]: TPasJSONInt32
- read GetKeyIndex;
- property Keys[const Index: TPasJSONSizeInt]: TPasJSONUTF8String read GetKey
- write SetKey;
- property Values[const Index: TPasJSONSizeInt]: TPasJSONItem read GetValue
- write SetValue;
- property Properties[const Key: TPasJSONUTF8String]: TPasJSONItem
- read GetProperty write SetProperty; default;
- end;
- TPasJSONItemArray = class(TPasJSONItem)
- public type
- PPasJSONItemArrayEnumerator = ^TPasJSONItemArrayEnumerator;
- TPasJSONItemArrayEnumerator = record
- private
- fOwner: TPasJSONItemArray;
- fIndex: TPasJSONSizeInt;
- function GetCurrent: TPasJSONItem; inline;
- public
- constructor Create(const aOwner: TPasJSONItemArray);
- function MoveNext: boolean; inline;
- property Current: TPasJSONItem read GetCurrent;
- end;
- private
- fItems: TPasJSONItems;
- fCount: TPasJSONInt32;
- function GetValue(const Index: TPasJSONInt32): TPasJSONItem;
- procedure SetValue(const Index: TPasJSONInt32; const Item: TPasJSONItem);
- public
- constructor Create;
- destructor Destroy; override;
- function GetEnumerator: TPasJSONItemArrayEnumerator; inline;
- procedure Clear;
- procedure Add(const AValue: TPasJSONItem);
- procedure Delete(const aIndex: TPasJSONSizeInt);
- procedure Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []); override;
- property Count: TPasJSONInt32 read fCount;
- property Items[const Index: TPasJSONInt32]: TPasJSONItem read GetValue
- write SetValue; default;
- end;
- PPasJSONModeFlag = ^TPasJSONModeFlag;
- TPasJSONModeFlag = (UnquotedKeys, Comments, ImplicitRootObject,
- OptionalCommas, EqualsForColon, MultilineStrings, HexadecimalNumbers);
- PPasJSONModeFlags = ^TPasJSONModeFlag;
- TPasJSONModeFlags = set of TPasJSONModeFlag;
- PPasJSONEncoding = ^TPasJSONEncoding;
- TPasJSONEncoding = (AutomaticDetection, Latin1, UTF8, UTF16LE, UTF16BE,
- UTF32LE, UTF32BE);
- TPasJSON = class
- public
- // Simplified JSON notation as in http://bitsquid.blogspot.de/2009/10/simplified-json-notation.html
- const
- SimplifiedJSONModeFlags: TPasJSONModeFlags = [TPasJSONModeFlag.UnquotedKeys,
- TPasJSONModeFlag.Comments, TPasJSONModeFlag.ImplicitRootObject,
- TPasJSONModeFlag.OptionalCommas, TPasJSONModeFlag.EqualsForColon,
- TPasJSONModeFlag.MultilineStrings];
- public
- class function StringQuote(const aString: TPasJSONUTF8String)
- : TPasJSONRawByteString; static;
- class function Parse(const aSource: TPasJSONRawByteString;
- const aModeFlags: TPasJSONModeFlags = [TPasJSONModeFlag.Comments];
- const aEncoding: TPasJSONEncoding = TPasJSONEncoding.AutomaticDetection)
- : TPasJSONItem; overload; static;
- class function Parse(const aStream: TStream;
- const aModeFlags: TPasJSONModeFlags = [TPasJSONModeFlag.Comments];
- const aEncoding: TPasJSONEncoding = TPasJSONEncoding.AutomaticDetection)
- : TPasJSONItem; overload; static;
- class function Stringify(const aJSONItem: TPasJSONItem;
- const aFormatting: boolean = false;
- const aModeFlags: TPasJSONModeFlags = []; const aLevel: TPasJSONInt32 = 0)
- : TPasJSONRawByteString; static;
- class procedure StringifyToStream(const aStream: TStream;
- const aJSONItem: TPasJSONItem; const aFormatting: boolean = false;
- const aModeFlags: TPasJSONModeFlags = [];
- const aLevel: TPasJSONInt32 = 0); static;
- class function GetNumber(const aItem: TPasJSONItem;
- const aDefault: TPasJSONDouble = 0.0): TPasJSONDouble; static;
- class function GetInt64(const aItem: TPasJSONItem;
- const aDefault: TPasJSONInt64 = 0): TPasJSONInt64; static;
- class function GetString(const aItem: TPasJSONItem;
- const aDefault: TPasJSONUTF8String = ''): TPasJSONUTF8String; static;
- class function GetBoolean(const aItem: TPasJSONItem;
- const aDefault: boolean = false): boolean; static;
- class function LoadBinaryFromStream(const aStream: TStream)
- : TPasJSONItem; static;
- class procedure SaveBinaryToStream(const aStream: TStream;
- const aJSONItem: TPasJSONItem); static;
- end;
- implementation
- uses
- PasDblStrUtils;
- // 0 1 2 3 4 5 6 7 8 9 a b c d e f
- const { UTF8CharSteps:array[AnsiChar] of byte=(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 1
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 2
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 3
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 4
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 5
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 6
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 7
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 8
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 9
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // a
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // b
- 1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // d
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // e
- 4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1); // f
- //0 1 2 3 4 5 6 7 8 9 a b c d e f }
- UTF8DFACharClasses: array [AnsiChar] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8);
- UTF8DFATransitions: array [byte] of byte = (0, 16, 32, 48, 80, 128, 112, 16,
- 16, 16, 64, 96, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 0, 16, 16, 16, 16, 16,
- 16, 16, 32, 16, 16, 16, 16, 16, 32, 16, 32, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 32, 16, 16, 16,
- 16, 16, 16, 16, 32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 48,
- 16, 48, 16, 16, 16, 16, 16, 16, 16, 48, 16, 16, 16, 16, 16, 48, 16, 48, 16,
- 16, 16, 16, 16, 16, 16, 48, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16);
- suDONOTKNOW = -1;
- suNOUTF8 = 0;
- suPOSSIBLEUTF8 = 1;
- suISUTF8 = 2;
- ucACCEPT = 0;
- ucERROR = 16;
- function GetNextUTF8Char(const aString: PPasJSONRawByteChar;
- const aStringLength: TPasJSONInt32; var aCodeUnit: TPasJSONInt32)
- : TPasJSONUInt32;
- var
- StartCodeUnit, Value, CharClass, State: TPasJSONUInt32;
- begin
- result := 0;
- if (aCodeUnit > 0) and (aCodeUnit <= aStringLength) then
- begin
- dec(aCodeUnit);
- StartCodeUnit := aCodeUnit;
- State := ucACCEPT;
- while aCodeUnit < aStringLength do
- begin
- Value := byte(AnsiChar(aString[aCodeUnit]));
- inc(aCodeUnit);
- CharClass := UTF8DFACharClasses[AnsiChar(Value)];
- if State = ucACCEPT then
- begin
- result := Value and ($FF shr CharClass);
- end
- else
- begin
- result := (result shl 6) or (Value and $3F);
- end;
- State := UTF8DFATransitions[State + CharClass];
- if State <= ucERROR then
- begin
- break;
- end;
- end;
- if State <> ucACCEPT then
- begin
- result := byte(AnsiChar(aString[StartCodeUnit]));
- aCodeUnit := StartCodeUnit + 1;
- end;
- inc(aCodeUnit);
- end;
- end;
- function ConvertUTF16ToUTF8(const aUTF16String: TPasJSONUTF16String)
- : TPasJSONUTF8String;
- var
- i, j: TPasJSONInt32;
- w: UInt16;
- u4c: TPasJSONUInt32;
- begin
- result := '';
- j := 0;
- i := 1;
- while i <= length(aUTF16String) do
- begin
- w := UInt16(TPasJSONUTF16Char(aUTF16String[i]));
- if (w <= $D7FF) or (w >= $E000) then
- begin
- u4c := w;
- inc(i);
- end
- else if ((i + 1) <= length(aUTF16String)) and ((w >= $D800) and (w <= $DBFF)
- ) and ((UInt16(aUTF16String[i + 1]) >= $DC00) and
- (UInt16(aUTF16String[i + 1]) <= $DFFF)) then
- begin
- u4c := (TPasJSONUInt32(TPasJSONUInt32(w and $3FF) shl 10) or
- TPasJSONUInt32(UInt16(TPasJSONUTF16Char(aUTF16String[i + 1])) and $3FF)
- ) + $10000;
- inc(i, 2);
- end
- else
- begin
- u4c := $FFFD;
- inc(i);
- end;
- if u4c <= $7F then
- begin
- inc(j);
- end
- else if u4c <= $7FF then
- begin
- inc(j, 2);
- end
- else if u4c <= $FFFF then
- begin
- inc(j, 3);
- end
- else if u4c <= $1FFFFF then
- begin
- inc(j, 4);
- end
- else
- begin
- inc(j, 3);
- end;
- end;
- SetLength(result, j);
- j := 1;
- i := 1;
- while i <= length(aUTF16String) do
- begin
- w := UInt16(TPasJSONUTF16Char(aUTF16String[i]));
- if (w <= $D7FF) or (w >= $E000) then
- begin
- u4c := w;
- inc(i);
- end
- else if ((i + 1) <= length(aUTF16String)) and ((w >= $D800) and (w <= $DBFF)
- ) and ((UInt16(aUTF16String[i + 1]) >= $DC00) and
- (UInt16(aUTF16String[i + 1]) <= $DFFF)) then
- begin
- u4c := (TPasJSONUInt32(TPasJSONUInt32(w and $3FF) shl 10) or
- TPasJSONUInt32(UInt16(TPasJSONUTF16Char(aUTF16String[i + 1])) and $3FF)
- ) + $10000;
- inc(i, 2);
- end
- else
- begin
- u4c := $FFFD;
- inc(i);
- end;
- if u4c <= $7F then
- begin
- result[j] := AnsiChar(byte(u4c));
- inc(j);
- end
- else if u4c <= $7FF then
- begin
- result[j] := AnsiChar(byte($C0 or ((u4c shr 6) and $1F)));
- result[j + 1] := AnsiChar(byte($80 or (u4c and $3F)));
- inc(j, 2);
- end
- else if u4c <= $FFFF then
- begin
- result[j] := AnsiChar(byte($E0 or ((u4c shr 12) and $0F)));
- result[j + 1] := AnsiChar(byte($80 or ((u4c shr 6) and $3F)));
- result[j + 2] := AnsiChar(byte($80 or (u4c and $3F)));
- inc(j, 3);
- end
- else if u4c <= $1FFFFF then
- begin
- result[j] := AnsiChar(byte($F0 or ((u4c shr 18) and $07)));
- result[j + 1] := AnsiChar(byte($80 or ((u4c shr 12) and $3F)));
- result[j + 2] := AnsiChar(byte($80 or ((u4c shr 6) and $3F)));
- result[j + 3] := AnsiChar(byte($80 or (u4c and $3F)));
- inc(j, 4);
- end
- else
- begin
- u4c := $FFFD;
- result[j] := AnsiChar(byte($E0 or (u4c shr 12)));
- result[j + 1] := AnsiChar(byte($80 or ((u4c shr 6) and $3F)));
- result[j + 2] := AnsiChar(byte($80 or (u4c and $3F)));
- inc(j, 3);
- end;
- end;
- end;
- constructor EPasJSONSyntaxError.Create(const aMessage: string;
- const aPosition: TPasJSONSizeInt);
- begin
- inherited Create(aMessage);
- fPosition := aPosition;
- end;
- constructor TPasJSONItem.Create;
- begin
- inherited Create;
- end;
- destructor TPasJSONItem.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TPasJSONItem.Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []);
- begin
- if not(assigned(aWith) and (aWith is TPasJSONItem)) then
- begin
- raise EPasJSONMergeError.Create('Incompatible data type');
- end;
- end;
- constructor TPasJSONItemNull.Create;
- begin
- inherited Create;
- end;
- destructor TPasJSONItemNull.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TPasJSONItemNull.Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []);
- begin
- if not(assigned(aWith) and (aWith is TPasJSONItemNull)) then
- begin
- raise EPasJSONMergeError.Create('Incompatible data type');
- end;
- end;
- constructor TPasJSONItemBoolean.Create(const AValue: boolean);
- begin
- inherited Create;
- fValue := AValue;
- end;
- destructor TPasJSONItemBoolean.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TPasJSONItemBoolean.Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []);
- begin
- if not(assigned(aWith) and (aWith is TPasJSONItemBoolean)) then
- begin
- raise EPasJSONMergeError.Create('Incompatible data type');
- end;
- fValue := TPasJSONItemBoolean(aWith).Value;
- end;
- constructor TPasJSONItemNumber.Create(const AValue: TPasJSONDouble);
- begin
- inherited Create;
- fValue := AValue;
- end;
- destructor TPasJSONItemNumber.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TPasJSONItemNumber.Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []);
- begin
- if not(assigned(aWith) and (aWith is TPasJSONItemNumber)) then
- begin
- raise EPasJSONMergeError.Create('Incompatible data type');
- end;
- fValue := TPasJSONItemNumber(aWith).Value;
- end;
- constructor TPasJSONItemString.Create(const AValue: TPasJSONUTF8String);
- begin
- inherited Create;
- fValue := AValue;
- end;
- destructor TPasJSONItemString.Destroy;
- begin
- fValue := '';
- inherited Destroy;
- end;
- procedure TPasJSONItemString.Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []);
- begin
- if not(assigned(aWith) and (aWith is TPasJSONItemString)) then
- begin
- raise EPasJSONMergeError.Create('Incompatible data type');
- end;
- fValue := TPasJSONItemString(aWith).Value;
- end;
- constructor TPasJSONItemObjectProperty.Create;
- begin
- inherited Create;
- fKey := '';
- fValue := nil;
- end;
- destructor TPasJSONItemObjectProperty.Destroy;
- begin
- fKey := '';
- FreeAndNil(fValue);
- inherited Destroy;
- end;
- constructor TPasJSONItemObject.TPasJSONItemObjectEnumerator.Create
- (const aOwner: TPasJSONItemObject);
- begin
- fOwner := aOwner;
- fIndex := -1;
- end;
- function TPasJSONItemObject.TPasJSONItemObjectEnumerator.MoveNext: boolean;
- begin
- inc(fIndex);
- result := (fIndex >= 0) and (fIndex < fOwner.fCount);
- end;
- function TPasJSONItemObject.TPasJSONItemObjectEnumerator.GetCurrent
- : TPasJSONItemObjectProperty;
- begin
- result := fOwner.fProperties[fIndex];
- end;
- constructor TPasJSONItemObject.Create;
- begin
- inherited Create;
- fProperties := nil;
- fCount := 0;
- end;
- destructor TPasJSONItemObject.Destroy;
- var
- Index: TPasJSONInt32;
- begin
- for Index := 0 to fCount - 1 do
- begin
- FreeAndNil(fProperties[Index]);
- end;
- SetLength(fProperties, 0);
- inherited Destroy;
- end;
- function TPasJSONItemObject.GetKeyIndex(const aKey: TPasJSONUTF8String)
- : TPasJSONInt32;
- var
- Index: TPasJSONInt32;
- begin
- for Index := 0 to fCount - 1 do
- begin
- if fProperties[Index].Key = aKey then
- begin
- result := Index;
- exit;
- end;
- end;
- result := -1;
- end;
- function TPasJSONItemObject.GetKey(const aIndex: TPasJSONSizeInt)
- : TPasJSONUTF8String;
- begin
- if (aIndex >= 0) and (aIndex < fCount) then
- begin
- result := fProperties[aIndex].Key;
- end
- else
- begin
- result := '';
- end;
- end;
- procedure TPasJSONItemObject.SetKey(const aIndex: TPasJSONSizeInt;
- const aKey: TPasJSONUTF8String);
- begin
- if (aIndex >= 0) and (aIndex < fCount) then
- begin
- fProperties[aIndex].Key := aKey;
- end;
- end;
- function TPasJSONItemObject.GetValue(const aIndex: TPasJSONSizeInt)
- : TPasJSONItem;
- begin
- if (aIndex >= 0) and (aIndex < fCount) then
- begin
- result := fProperties[aIndex].Value;
- end
- else
- begin
- result := nil;
- end;
- end;
- procedure TPasJSONItemObject.SetValue(const aIndex: TPasJSONSizeInt;
- const aItem: TPasJSONItem);
- begin
- if (aIndex >= 0) and (aIndex < fCount) then
- begin
- fProperties[aIndex].Value := aItem;
- end;
- end;
- function TPasJSONItemObject.GetProperty(const aKey: TPasJSONUTF8String)
- : TPasJSONItem;
- begin
- result := GetValue(GetKeyIndex(aKey));
- end;
- procedure TPasJSONItemObject.SetProperty(const aKey: TPasJSONUTF8String;
- const aItem: TPasJSONItem);
- begin
- SetValue(GetKeyIndex(aKey), aItem);
- end;
- function TPasJSONItemObject.GetEnumerator
- : TPasJSONItemObject.TPasJSONItemObjectEnumerator;
- begin
- result := TPasJSONItemObject.TPasJSONItemObjectEnumerator.Create(self);
- end;
- procedure TPasJSONItemObject.Clear;
- var
- Index: TPasJSONInt32;
- begin
- for Index := 0 to fCount - 1 do
- begin
- FreeAndNil(fProperties[Index]);
- end;
- SetLength(fProperties, 0);
- fCount := 0;
- end;
- procedure TPasJSONItemObject.Add(const aKey: TPasJSONUTF8String;
- const AValue: TPasJSONItem);
- var
- Index: TPasJSONSizeInt;
- ObjectProperty: TPasJSONItemObjectProperty;
- begin
- Index := fCount;
- inc(fCount);
- if fCount >= length(fProperties) then
- begin
- SetLength(fProperties, fCount * 2);
- end;
- ObjectProperty := TPasJSONItemObjectProperty.Create;
- fProperties[Index] := ObjectProperty;
- ObjectProperty.Key := aKey;
- ObjectProperty.Value := AValue;
- end;
- procedure TPasJSONItemObject.Delete(const aIndex: TPasJSONSizeInt);
- begin
- if (aIndex >= 0) and (aIndex < fCount) then
- begin
- FreeAndNil(fProperties[aIndex]);
- dec(fCount);
- Move(fProperties[aIndex + 1], fProperties[aIndex],
- fCount * SizeOf(TPasJSONItemObjectProperty));
- end;
- end;
- procedure TPasJSONItemObject.Delete(const aKey: TPasJSONUTF8String);
- begin
- Delete(GetKeyIndex(aKey));
- end;
- procedure TPasJSONItemObject.Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []);
- var
- Index, KeyIndex: TPasJSONSizeInt;
- SrcProperty, OurProperty: TPasJSONItemObjectProperty;
- NewItem: TPasJSONItem;
- begin
- if not(assigned(aWith) and (aWith is TPasJSONItemObject)) then
- begin
- raise EPasJSONMergeError.Create('Incompatible data type');
- end;
- for Index := 0 to TPasJSONItemObject(aWith).Count - 1 do
- begin
- SrcProperty := TPasJSONItemObject(aWith).fProperties[Index];
- if assigned(SrcProperty.Value) then
- begin
- KeyIndex := GetKeyIndex(SrcProperty.Key);
- if KeyIndex >= 0 then
- begin
- OurProperty := fProperties[KeyIndex];
- if (TPasJSONMergeFlag.ForceObjectPropertyValueDestinationType in aFlags)
- and ((not assigned(OurProperty.Value)) or
- (assigned(OurProperty.Value) and (OurProperty.Value.ClassType <>
- SrcProperty.Value.ClassType))) then
- begin
- if assigned(OurProperty.Value) then
- begin
- OurProperty.Value.Free;
- end;
- OurProperty.Value := TPasJSONItem(SrcProperty.Value.ClassType.Create);
- end;
- if assigned(OurProperty.Value) and
- (OurProperty.Value.ClassType = SrcProperty.Value.ClassType) then
- begin
- OurProperty.Value.Merge(SrcProperty.Value, aFlags);
- end;
- end
- else
- begin
- NewItem := nil;
- try
- NewItem := TPasJSONItem(SrcProperty.Value.ClassType.Create);
- NewItem.Merge(SrcProperty.Value, aFlags);
- Add(SrcProperty.Key, NewItem);
- except
- NewItem.Free;
- raise;
- end;
- end;
- end;
- end;
- end;
- constructor TPasJSONItemArray.TPasJSONItemArrayEnumerator.Create
- (const aOwner: TPasJSONItemArray);
- begin
- fOwner := aOwner;
- fIndex := -1;
- end;
- function TPasJSONItemArray.TPasJSONItemArrayEnumerator.MoveNext: boolean;
- begin
- inc(fIndex);
- result := (fIndex >= 0) and (fIndex < fOwner.fCount);
- end;
- function TPasJSONItemArray.TPasJSONItemArrayEnumerator.GetCurrent: TPasJSONItem;
- begin
- result := fOwner.fItems[fIndex];
- end;
- constructor TPasJSONItemArray.Create;
- begin
- inherited Create;
- fItems := nil;
- fCount := 0;
- end;
- destructor TPasJSONItemArray.Destroy;
- var
- Index: TPasJSONInt32;
- begin
- for Index := 0 to fCount - 1 do
- begin
- FreeAndNil(fItems[Index]);
- end;
- SetLength(fItems, 0);
- inherited Destroy;
- end;
- function TPasJSONItemArray.GetValue(const Index: TPasJSONInt32): TPasJSONItem;
- begin
- if (Index >= 0) and (Index < fCount) then
- begin
- result := fItems[Index];
- end
- else
- begin
- result := nil;
- end;
- end;
- procedure TPasJSONItemArray.SetValue(const Index: TPasJSONInt32;
- const Item: TPasJSONItem);
- begin
- if (Index >= 0) and (Index < fCount) then
- begin
- fItems[Index] := Item;
- end;
- end;
- function TPasJSONItemArray.GetEnumerator
- : TPasJSONItemArray.TPasJSONItemArrayEnumerator;
- begin
- result := TPasJSONItemArray.TPasJSONItemArrayEnumerator.Create(self);
- end;
- procedure TPasJSONItemArray.Clear;
- var
- Index: TPasJSONInt32;
- begin
- for Index := 0 to fCount - 1 do
- begin
- FreeAndNil(fItems[Index]);
- end;
- SetLength(fItems, 0);
- fCount := 0;
- end;
- procedure TPasJSONItemArray.Add(const AValue: TPasJSONItem);
- var
- Index: TPasJSONSizeInt;
- begin
- Index := fCount;
- inc(fCount);
- if fCount >= length(fItems) then
- begin
- SetLength(fItems, fCount * 2);
- end;
- fItems[Index] := AValue;
- end;
- procedure TPasJSONItemArray.Delete(const aIndex: TPasJSONSizeInt);
- begin
- if (aIndex >= 0) and (aIndex < fCount) then
- begin
- FreeAndNil(fItems[aIndex]);
- dec(fCount);
- Move(fItems[aIndex + 1], fItems[aIndex], fCount * SizeOf(TPasJSONItem));
- end;
- end;
- procedure TPasJSONItemArray.Merge(const aWith: TPasJSONItem;
- const aFlags: TPasJSONMergeFlags = []);
- var
- Index: TPasJSONSizeInt;
- Item, NewItem: TPasJSONItem;
- begin
- if not(assigned(aWith) and (aWith is TPasJSONItemArray)) then
- begin
- raise EPasJSONMergeError.Create('Incompatible data type');
- end;
- for Index := 0 to TPasJSONItemArray(aWith).Count - 1 do
- begin
- Item := TPasJSONItemArray(aWith).Items[Index];
- if assigned(Item) then
- begin
- NewItem := TPasJSONItem(Item.ClassType.Create);
- Add(NewItem);
- NewItem.Merge(Item, aFlags);
- end;
- end;
- end;
- const
- HexChars: array [boolean, 0 .. 15] of AnsiChar = (('0', '1', '2', '3', '4',
- '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'),
- ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
- 'E', 'F'));
- class function TPasJSON.StringQuote(const aString: TPasJSONUTF8String)
- : TPasJSONRawByteString;
- var
- i, l: TPasJSONInt32;
- c, t: TPasJSONUInt32;
- begin
- result := '"';
- i := 1;
- l := length(aString);
- while i <= l do
- begin
- case aString[i] of
- '"', '\':
- begin
- result := result + '\' + AnsiChar(aString[i]);
- inc(i);
- end;
- #$08:
- begin
- result := result + '\b';
- inc(i);
- end;
- #$09:
- begin
- result := result + '\t';
- inc(i);
- end;
- #$0a:
- begin
- result := result + '\n';
- inc(i);
- end;
- #$0b:
- begin
- result := result + '\v';
- inc(i);
- end;
- #$0c:
- begin
- result := result + '\f';
- inc(i);
- end;
- #$0d:
- begin
- result := result + '\r';
- inc(i);
- end;
- #$00 .. #$07, #$0e .. #$1f, #$7e .. #$7f:
- begin
- c := byte(AnsiChar(aString[i]));
- result := result + '\u00' + HexChars[false, (c shr 4) and $F] +
- HexChars[false, c and $F];
- inc(i);
- end;
- #$80 .. #$ff:
- begin
- c := GetNextUTF8Char(PAnsiChar(@aString[1]), l, i);
- case c of
- $0000 .. $D7FF, $F000 .. $FFFC:
- begin
- result := result + '\u' + HexChars[false, (c shr 12) and $F] +
- HexChars[false, (c shr 8) and $F] + HexChars
- [false, (c shr 4) and $F] + HexChars[false, c and $F];
- end;
- $100000 .. $10FFFF:
- begin
- dec(c, $100000);
- t := (c shr 10) or $D800;
- result := result + '\u' + HexChars[false, (t shr 12) and $F] +
- HexChars[false, (t shr 8) and $F] + HexChars
- [false, (t shr 4) and $F] + HexChars[false, t and $F];
- t := (c and $3FF) or $DC00;
- result := result + '\u' + HexChars[false, (t shr 12) and $F] +
- HexChars[false, (t shr 8) and $F] + HexChars
- [false, (t shr 4) and $F] + HexChars[false, t and $F];
- end;
- else { -$d800..$dfff,$fffd..$ffff,$110000..$ffffffff: }
- begin
- result := result + '\ufffd';
- end;
- end;
- end;
- else
- begin
- result := result + AnsiChar(aString[i]);
- inc(i);
- end;
- end;
- end;
- result := result + '"';
- end;
- class function TPasJSON.Parse(const aSource: TPasJSONRawByteString;
- const aModeFlags: TPasJSONModeFlags = [TPasJSONModeFlag.Comments];
- const aEncoding: TPasJSONEncoding = TPasJSONEncoding.AutomaticDetection)
- : TPasJSONItem;
- var
- Position: TPasJSONInt32;
- CurrentChar: TPasJSONUInt32;
- CharEOF: boolean;
- Encoding: TPasJSONEncoding;
- procedure NextChar;
- const
- UTF16Shifts: array [TPasJSONEncoding.UTF16LE .. TPasJSONEncoding.UTF16BE,
- 0 .. 1] of TPasJSONInt32 = ((0, 8), (8, 0));
- var
- Temp: TPasJSONUInt32;
- begin
- if Position <= length(aSource) then
- begin
- case Encoding of
- TPasJSONEncoding.UTF8:
- begin
- CurrentChar := GetNextUTF8Char(PAnsiChar(@aSource[1]),
- length(aSource), Position);
- end;
- TPasJSONEncoding.UTF16LE, TPasJSONEncoding.UTF16BE:
- begin
- if (Position + 1) <= length(aSource) then
- begin
- CurrentChar := (TPasJSONUInt32(byte(AnsiChar(aSource[Position])))
- shl UTF16Shifts[Encoding, 0]) or
- (TPasJSONUInt32(byte(AnsiChar(aSource[Position + 1])))
- shl UTF16Shifts[Encoding, 1]);
- inc(Position, 2);
- if ((CurrentChar >= $D800) and (CurrentChar <= $DBFF)) and
- ((Position + 1) <= length(aSource)) then
- begin
- Temp := (TPasJSONUInt32(byte(AnsiChar(aSource[Position])))
- shl UTF16Shifts[Encoding, 0]) or
- (TPasJSONUInt32(byte(AnsiChar(aSource[Position + 1])))
- shl UTF16Shifts[Encoding, 1]);
- if (Temp >= $DC00) and (Temp <= $DFFF) then
- begin
- CurrentChar :=
- (TPasJSONUInt32(TPasJSONUInt32(CurrentChar and $3FF) shl 10)
- or TPasJSONUInt32(Temp and $3FF)) + $10000;
- inc(Position, 2);
- end
- else
- begin
- CurrentChar := $FFFD;
- end;
- end
- else if not((CurrentChar <= $D7FF) or (CurrentChar >= $E000)) then
- begin
- CurrentChar := $FFFD;
- end;
- end
- else
- begin
- inc(Position);
- CurrentChar := 0;
- CharEOF := true;
- end;
- end;
- TPasJSONEncoding.UTF32LE:
- begin
- if (Position + 3) <= length(aSource) then
- begin
- CurrentChar := (TPasJSONUInt32(byte(AnsiChar(aSource[Position])))
- shl 0) or (TPasJSONUInt32(byte(AnsiChar(aSource[Position + 1])))
- shl 8) or (TPasJSONUInt32(byte(AnsiChar(aSource[Position + 2])))
- shl 16) or
- (TPasJSONUInt32(byte(AnsiChar(aSource[Position + 3]))) shl 24);
- inc(Position, 4);
- end
- else
- begin
- inc(Position);
- CurrentChar := 0;
- CharEOF := true;
- end;
- end;
- TPasJSONEncoding.UTF32BE:
- begin
- if (Position + 3) <= length(aSource) then
- begin
- CurrentChar := (TPasJSONUInt32(byte(AnsiChar(aSource[Position])))
- shl 24) or (TPasJSONUInt32(byte(AnsiChar(aSource[Position + 1]))
- ) shl 16) or
- (TPasJSONUInt32(byte(AnsiChar(aSource[Position + 2]))) shl 8) or
- (TPasJSONUInt32(byte(AnsiChar(aSource[Position + 3]))) shl 0);
- inc(Position, 4);
- end
- else
- begin
- inc(Position);
- CurrentChar := 0;
- CharEOF := true;
- end;
- end;
- else
- begin
- CurrentChar := byte(AnsiChar(aSource[Position]));
- inc(Position);
- end;
- end;
- end
- else
- begin
- CurrentChar := 0;
- CharEOF := true;
- end;
- end;
- procedure JSONError;
- begin
- raise EPasJSONSyntaxError.Create('Parser error at byte position ' +
- IntToStr(Position), Position);
- end;
- procedure SkipWhite;
- var
- LastChar: TPasJSONUInt32;
- begin
- while not CharEOF do
- begin
- case CurrentChar of
- $0009, $000A, $000D, $0020:
- begin
- NextChar;
- end;
- ord('/'):
- begin
- if TPasJSONModeFlag.Comments in aModeFlags then
- begin
- NextChar;
- case CurrentChar of
- ord('/'):
- begin
- NextChar;
- while not(CharEOF or ((CurrentChar = $000A) or
- (CurrentChar = $000D))) do
- begin
- NextChar;
- end;
- end;
- ord('*'):
- begin
- NextChar;
- LastChar := 0;
- while not(CharEOF or ((LastChar = ord('*')) and
- (CurrentChar = ord('/')))) do
- begin
- LastChar := CurrentChar;
- NextChar;
- end;
- if CurrentChar = ord('/') then
- begin
- NextChar;
- end;
- end;
- else
- begin
- JSONError;
- end;
- end;
- end
- else
- begin
- JSONError;
- end;
- end;
- else
- begin
- break;
- end;
- end;
- end;
- end;
- function IsChar(c: TPasJSONUTF16Char): boolean;
- begin
- result := (not CharEOF) and (CurrentChar = UInt16(c));
- end;
- procedure ExpectChar(c: TPasJSONUTF16Char);
- begin
- if IsChar(c) then
- begin
- NextChar;
- end
- else
- begin
- JSONError;
- end;
- end;
- function ParseValue(const ImplicitRootObject: boolean): TPasJSONItem;
- function ParseString: TPasJSONItem;
- var
- w: TPasJSONUTF16String;
- // <= because of easy correct handling of UTF16 surrogates (see ECMAScript and JSON specs)
- wl: TPasJSONInt32;
- wc: TPasJSONUInt32;
- procedure AddChar(c: TPasJSONUInt32);
- var
- cl: TPasJSONInt32;
- begin
- if (c >= $100000) and (c <= $10FFFF) then
- begin
- cl := 2;
- end
- else
- begin
- cl := 1;
- end;
- if (wl + cl) > length(w) then
- begin
- SetLength(w, (wl + cl) shl 1);
- end;
- if c <= $D7FF then
- begin
- inc(wl);
- w[wl] := TPasJSONUTF16Char(UInt16(c));
- end
- else if c <= $DFFF then
- begin
- inc(wl);
- w[wl] := TPasJSONUTF16Char(UInt16($FFFD));
- end
- else if c <= $FFFD then
- begin
- inc(wl);
- w[wl] := TPasJSONUTF16Char(UInt16(c));
- end
- else if c <= $FFFF then
- begin
- inc(wl);
- w[wl] := TPasJSONUTF16Char(UInt16($FFFD));
- end
- else if c <= $10FFFF then
- begin
- dec(c, $10000);
- inc(wl);
- w[wl] := TPasJSONUTF16Char(UInt16((c shr 10) or $D800));
- inc(wl);
- w[wl] := TPasJSONUTF16Char(UInt16((c and $3FF) or $DC00));
- end
- else
- begin
- inc(wl);
- w[wl] := TPasJSONUTF16Char(UInt16($FFFD));
- end;
- end;
- begin
- result := nil;
- w := '';
- try
- try
- SetLength(w, 512);
- wl := 0;
- if IsChar('"') then
- begin
- NextChar;
- while not(CharEOF or IsChar('"')) do
- begin
- case CurrentChar of
- $0000 .. $001F:
- begin
- if TPasJSONModeFlag.MultilineStrings in aModeFlags then
- begin
- AddChar(CurrentChar);
- NextChar;
- end
- else
- begin
- JSONError;
- end;
- end;
- ord('\'):
- begin
- NextChar;
- case CurrentChar of
- ord('"'), ord('\'), ord('/'):
- begin
- AddChar(CurrentChar);
- NextChar;
- end;
- ord('b'):
- begin
- AddChar($0008);
- NextChar;
- end;
- ord('f'):
- begin
- AddChar($000C);
- NextChar;
- end;
- ord('n'):
- begin
- AddChar($000A);
- NextChar;
- end;
- ord('r'):
- begin
- AddChar($000D);
- NextChar;
- end;
- ord('t'):
- begin
- AddChar($0009);
- NextChar;
- end;
- ord('u'):
- begin
- NextChar;
- if (((CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9'))) or
- ((CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f'))) or
- ((CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')))) and not CharEOF then
- begin
- if (CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9')) then
- begin
- wc := UInt16(CurrentChar) - ord('0');
- end
- else if (CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f')) then
- begin
- wc := (UInt16(CurrentChar) - ord('a')) + $A;
- end
- else if (CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')) then
- begin
- wc := (UInt16(CurrentChar) - ord('A')) + $A;
- end
- else
- begin
- wc := 0;
- end;
- wc := wc shl 4;
- NextChar;
- if (((CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9'))) or
- ((CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f'))) or
- ((CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')))) and not CharEOF then
- begin
- if (CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9')) then
- begin
- wc := wc or
- UInt16(UInt16(CurrentChar) - ord('0'));
- end
- else if (CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f')) then
- begin
- wc := wc or
- UInt16((UInt16(CurrentChar) - ord('a')) + $A);
- end
- else if (CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')) then
- begin
- wc := wc or
- UInt16((UInt16(CurrentChar) - ord('A')) + $A);
- end;
- wc := wc shl 4;
- NextChar;
- if (((CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9'))) or
- ((CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f'))) or
- ((CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')))) and not CharEOF then
- begin
- if (CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9')) then
- begin
- wc := wc or
- UInt16(UInt16(CurrentChar) - ord('0'));
- end
- else if (CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f')) then
- begin
- wc := wc or
- UInt16((UInt16(CurrentChar) -
- ord('a')) + $A);
- end
- else if (CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')) then
- begin
- wc := wc or
- UInt16((UInt16(CurrentChar) -
- ord('A')) + $A);
- end;
- wc := wc shl 4;
- NextChar;
- if (((CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9'))) or
- ((CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f'))) or
- ((CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')))) and not CharEOF
- then
- begin
- if (CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9')) then
- begin
- wc := wc or
- UInt16(UInt16(CurrentChar) - ord('0'));
- end
- else if (CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f')) then
- begin
- wc := wc or
- UInt16((UInt16(CurrentChar) -
- ord('a')) + $A);
- end
- else if (CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')) then
- begin
- wc := wc or
- UInt16((UInt16(CurrentChar) -
- ord('A')) + $A);
- end;
- AddChar(wc);
- NextChar;
- end
- else
- begin
- JSONError;
- end;
- end
- else
- begin
- JSONError;
- end;
- end
- else
- begin
- JSONError;
- end;
- end
- else
- begin
- JSONError;
- end;
- end;
- else
- begin
- JSONError;
- end;
- end;
- end;
- else
- begin
- AddChar(CurrentChar);
- NextChar;
- end;
- end;
- end;
- ExpectChar('"');
- SetLength(w, wl);
- result := TPasJSONItemString.Create(ConvertUTF16ToUTF8(w));
- end
- else
- begin
- JSONError;
- end;
- SkipWhite;
- except
- FreeAndNil(result);
- raise;
- end;
- finally
- w := '';
- end;
- end;
- function ParseNumber: TPasJSONItem;
- var
- s: TPasJSONRawByteString;
- OK: TPasDblStrUtilsBoolean;
- Value: TPasJSONDouble;
- IsHexadecimal: boolean;
- begin
- result := nil;
- s := '';
- try
- try
- if CharEOF then
- begin
- JSONError;
- end;
- case CurrentChar of
- ord('-'), ord('0') .. ord('9'):
- begin
- if IsChar('-') then
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- if CharEOF or not((CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9'))) then
- begin
- JSONError;
- end;
- end;
- IsHexadecimal := false;
- if (not CharEOF) and (CurrentChar = ord('0')) then
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- if (TPasJSONModeFlag.HexadecimalNumbers in aModeFlags) and
- ((not CharEOF) and (CurrentChar in [ord('x'), ord('X')]))
- then
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- while (not CharEOF) and
- (((CurrentChar >= ord('0')) and (CurrentChar <= ord('9')))
- or ((CurrentChar >= ord('a')) and (CurrentChar <= ord('f')
- )) or ((CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')))) do
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- end;
- IsHexadecimal := true;
- end
- else
- begin
- if (not CharEOF) and
- ((CurrentChar >= ord('0')) and (CurrentChar <= ord('9')))
- then
- begin
- JSONError;
- end;
- end;
- end
- else
- begin
- while (not CharEOF) and
- ((CurrentChar >= ord('0')) and (CurrentChar <= ord('9'))) do
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- end;
- end;
- if IsChar('.') then
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- if CharEOF or
- not(((CurrentChar >= ord('0')) and (CurrentChar <= ord('9'))
- ) or (IsHexadecimal and (((CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f'))) or ((CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')))))) then
- begin
- JSONError;
- end;
- while (not CharEOF) and
- (((CurrentChar >= ord('0')) and (CurrentChar <= ord('9')))
- or (IsHexadecimal and (((CurrentChar >= ord('a')) and
- (CurrentChar <= ord('f'))) or ((CurrentChar >= ord('A')) and
- (CurrentChar <= ord('F')))))) do
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- end;
- end;
- if (not CharEOF) and
- ((CurrentChar = ord('e')) or (CurrentChar = ord('E'))) then
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- if (not CharEOF) and
- ((CurrentChar = ord('-')) or (CurrentChar = ord('+'))) then
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- end;
- if CharEOF or not((CurrentChar >= ord('0')) and
- (CurrentChar <= ord('9'))) then
- begin
- JSONError;
- end;
- while (not CharEOF) and
- ((CurrentChar >= ord('0')) and (CurrentChar <= ord('9'))) do
- begin
- s := s + AnsiChar(byte(CurrentChar));
- NextChar;
- end;
- end;
- end
- else
- begin
- JSONError;
- end;
- end;
- if length(s) > 0 then
- begin
- OK := false;
- Value := ConvertStringToDouble(s, rmNearest, @OK);
- if not OK then
- begin
- JSONError;
- end;
- result := TPasJSONItemNumber.Create(Value);
- end;
- except
- FreeAndNil(result);
- raise;
- end;
- finally
- s := '';
- end;
- end;
- procedure ParseObjectProperty(DestinationObject: TPasJSONItemObject);
- var
- Key: TPasJSONUTF8String;
- Value: TPasJSONItem;
- begin
- result := nil;
- try
- if (TPasJSONModeFlag.UnquotedKeys in aModeFlags) and (not CharEOF) and
- ((CurrentChar = UInt16(TPasJSONUTF16Char('_'))) or
- ((CurrentChar >= UInt16(TPasJSONUTF16Char('a'))) and
- (CurrentChar <= UInt16(TPasJSONUTF16Char('z')))) or
- ((CurrentChar >= UInt16(TPasJSONUTF16Char('A'))) and
- (CurrentChar <= UInt16(TPasJSONUTF16Char('Z')))) or
- ((CurrentChar >= UInt16(TPasJSONUTF16Char('0'))) and
- (CurrentChar <= UInt16(TPasJSONUTF16Char('9'))))) then
- begin
- Key := TPasJSONUTF8String(AnsiChar(byte(CurrentChar)));
- NextChar;
- while ((CurrentChar = UInt16(TPasJSONUTF16Char('_'))) or
- ((CurrentChar >= UInt16(TPasJSONUTF16Char('a'))) and
- (CurrentChar <= UInt16(TPasJSONUTF16Char('z')))) or
- ((CurrentChar >= UInt16(TPasJSONUTF16Char('A'))) and
- (CurrentChar <= UInt16(TPasJSONUTF16Char('Z')))) or
- ((CurrentChar >= UInt16(TPasJSONUTF16Char('0'))) and
- (CurrentChar <= UInt16(TPasJSONUTF16Char('9'))))) do
- begin
- Key := Key + TPasJSONUTF8String(AnsiChar(byte(CurrentChar)));
- NextChar;
- end;
- SkipWhite;
- end
- else
- begin
- Value := ParseString;
- if assigned(Value) and (Value is TPasJSONItemString) then
- begin
- Key := TPasJSONItemString(Value).Value;
- FreeAndNil(Value);
- end
- else
- begin
- FreeAndNil(Value);
- JSONError;
- Key := '';
- end;
- end;
- SkipWhite;
- if TPasJSONModeFlag.EqualsForColon in aModeFlags then
- begin
- ExpectChar('=');
- end
- else
- begin
- ExpectChar(':');
- end;
- SkipWhite;
- Value := ParseValue(false);
- DestinationObject.Add(Key, Value);
- except
- FreeAndNil(result);
- raise;
- end;
- end;
- function ParseObject(const ImplicitRootObject: boolean): TPasJSONItem;
- begin
- result := TPasJSONItemObject.Create;
- try
- if not ImplicitRootObject then
- begin
- ExpectChar('{');
- end;
- SkipWhite;
- while not(CharEOF or ((not ImplicitRootObject) and IsChar('}'))) do
- begin
- ParseObjectProperty(TPasJSONItemObject(result));
- SkipWhite;
- if IsChar(',') then
- begin
- NextChar;
- SkipWhite;
- if (not(CharEOF or ImplicitRootObject)) and IsChar('}') then
- begin
- JSONError;
- end;
- end
- else if not(TPasJSONModeFlag.OptionalCommas in aModeFlags) then
- begin
- break;
- end;
- end;
- if not ImplicitRootObject then
- begin
- ExpectChar('}');
- end;
- except
- FreeAndNil(result);
- raise;
- end;
- end;
- function ParseArray: TPasJSONItem;
- begin
- result := TPasJSONItemArray.Create;
- try
- ExpectChar('[');
- SkipWhite;
- while not(CharEOF or IsChar(']')) do
- begin
- TPasJSONItemArray(result).Add(ParseValue(false));
- SkipWhite;
- if IsChar(',') then
- begin
- NextChar;
- SkipWhite;
- if (not CharEOF) and IsChar(']') then
- begin
- JSONError;
- end;
- end
- else if not(TPasJSONModeFlag.OptionalCommas in aModeFlags) then
- begin
- break;
- end;
- end;
- ExpectChar(']');
- except
- FreeAndNil(result);
- raise;
- end;
- end;
- procedure ExpectKeyword(const Keyword: TPasJSONUTF8String);
- var
- i: TPasJSONInt32;
- begin
- for i := 1 to length(Keyword) do
- begin
- ExpectChar(TPasJSONUTF16Char(Keyword[i]));
- end;
- end;
- begin
- result := nil;
- try
- SkipWhite;
- if CharEOF then
- begin
- JSONError;
- end;
- if ImplicitRootObject then
- begin
- result := ParseObject(true);
- end
- else
- begin
- case CurrentChar of
- ord('{'):
- begin
- result := ParseObject(false);
- end;
- ord('['):
- begin
- result := ParseArray;
- end;
- ord('"'):
- begin
- result := ParseString;
- end;
- ord('-'), ord('0') .. ord('9'):
- begin
- result := ParseNumber;
- end;
- ord('t'):
- begin
- ExpectKeyword('true');
- result := TPasJSONItemBoolean.Create(true);
- end;
- ord('f'):
- begin
- ExpectKeyword('false');
- result := TPasJSONItemBoolean.Create(false);
- end;
- ord('n'):
- begin
- ExpectKeyword('null');
- result := TPasJSONItemNull.Create;
- end;
- else
- begin
- JSONError;
- end;
- end;
- end;
- except
- FreeAndNil(result);
- raise;
- end;
- end;
- begin
- result := nil;
- try
- CharEOF := false;
- if aEncoding = TPasJSONEncoding.AutomaticDetection then
- begin
- if (length(aSource) >= 3) and ((byte(AnsiChar(aSource[1])) = $EF) and
- (byte(AnsiChar(aSource[2])) = $BB) and
- (byte(AnsiChar(aSource[3])) = $BF)) then
- begin
- Position := 4;
- Encoding := TPasJSONEncoding.UTF8;
- end
- else if (length(aSource) >= 4) and ((byte(AnsiChar(aSource[1])) = $FF) and
- (byte(AnsiChar(aSource[2])) = $FE) and
- (byte(AnsiChar(aSource[3])) = $00) and
- (byte(AnsiChar(aSource[4])) = $00)) then
- begin
- Position := 5;
- Encoding := TPasJSONEncoding.UTF32LE;
- end
- else if (length(aSource) >= 4) and ((byte(AnsiChar(aSource[1])) = $00) and
- (byte(AnsiChar(aSource[2])) = $00) and
- (byte(AnsiChar(aSource[3])) = $FE) and
- (byte(AnsiChar(aSource[4])) = $FF)) then
- begin
- Position := 5;
- Encoding := TPasJSONEncoding.UTF32BE;
- end
- else if (length(aSource) >= 2) and ((byte(AnsiChar(aSource[1])) = $FF) and
- (byte(AnsiChar(aSource[2])) = $FE)) then
- begin
- Position := 3;
- Encoding := TPasJSONEncoding.UTF16LE;
- end
- else if (length(aSource) >= 2) and ((byte(AnsiChar(aSource[1])) = $FE) and
- (byte(AnsiChar(aSource[2])) = $FF)) then
- begin
- Position := 3;
- Encoding := TPasJSONEncoding.UTF16BE;
- end
- else
- begin
- Position := 1;
- Encoding := TPasJSONEncoding.Latin1;
- end;
- end
- else
- begin
- Position := 1;
- Encoding := aEncoding;
- end;
- NextChar;
- result := ParseValue(TPasJSONModeFlag.ImplicitRootObject in aModeFlags);
- SkipWhite;
- if not CharEOF then
- begin
- JSONError;
- end;
- except
- FreeAndNil(result);
- raise;
- end;
- end;
- class function TPasJSON.Parse(const aStream: TStream;
- const aModeFlags: TPasJSONModeFlags = [TPasJSONModeFlag.Comments];
- const aEncoding: TPasJSONEncoding = TPasJSONEncoding.AutomaticDetection)
- : TPasJSONItem;
- var
- StringValue: TPasJSONRawByteString;
- begin
- StringValue := '';
- try
- SetLength(StringValue, aStream.Size);
- if length(StringValue) > 0 then
- begin
- if aStream.Seek(0, soBeginning) <> 0 then
- begin
- raise EInOutError.Create('Stream seek error');
- end;
- aStream.ReadBuffer(StringValue[1], aStream.Size);
- end;
- result := TPasJSON.Parse(StringValue, aModeFlags, aEncoding);
- finally
- StringValue := '';
- end;
- end;
- class function TPasJSON.Stringify(const aJSONItem: TPasJSONItem;
- const aFormatting: boolean = false; const aModeFlags: TPasJSONModeFlags = [];
- const aLevel: TPasJSONInt32 = 0): TPasJSONRawByteString;
- function IsIdentifier(const pKey: TPasJSONUTF8String): boolean;
- var
- Index: TPasJSONInt32;
- begin
- result := length(pKey) > 0;
- for Index := 1 to length(pKey) do
- begin
- case UInt16(TPasJSONUTF16Char(pKey[Index])) of
- UInt16(TPasJSONUTF16Char('_')), UInt16(TPasJSONUTF16Char('a'))
- .. UInt16(TPasJSONUTF16Char('z')), UInt16(TPasJSONUTF16Char('A'))
- .. UInt16(TPasJSONUTF16Char('Z')), UInt16(TPasJSONUTF16Char('0'))
- .. UInt16(TPasJSONUTF16Char('9')):
- begin
- end;
- else
- begin
- result := false;
- break;
- end;
- end;
- end;
- end;
- var
- Index, Count, Ident, LevelOffset: TPasJSONInt32;
- IntegerValue: Int64;
- Key: TPasJSONUTF8String;
- begin
- if assigned(aJSONItem) then
- begin
- if aJSONItem is TPasJSONItemNull then
- begin
- result := 'null';
- end
- else if aJSONItem is TPasJSONItemBoolean then
- begin
- if TPasJSONItemBoolean(aJSONItem).Value then
- begin
- result := 'true';
- end
- else
- begin
- result := 'false';
- end;
- end
- else if aJSONItem is TPasJSONItemNumber then
- begin
- if IsInfinite(TPasJSONItemNumber(aJSONItem).Value) then
- begin
- if (TPasJSONUInt64(Pointer(@TPasJSONItemNumber(aJSONItem).Value)^)
- shr 63) <> 0 then
- begin
- result := TPasJSON.StringQuote('-Infinity');
- end
- else
- begin
- result := TPasJSON.StringQuote('Infinity');
- end;
- end
- else if IsNaN(TPasJSONItemNumber(aJSONItem).Value) then
- begin
- if (TPasJSONUInt64(Pointer(@TPasJSONItemNumber(aJSONItem).Value)^)
- shr 63) <> 0 then
- begin
- result := TPasJSON.StringQuote('-NaN');
- end
- else
- begin
- result := TPasJSON.StringQuote('NaN');
- end;
- end
- else
- begin
- IntegerValue := trunc(TPasJSONItemNumber(aJSONItem).Value);
- if TPasJSONItemNumber(aJSONItem).Value = IntegerValue then
- begin
- Str(IntegerValue, result);
- end
- else
- begin
- result := ConvertDoubleToString(TPasJSONItemNumber(aJSONItem).Value,
- omStandard, 0);
- end;
- end;
- end
- else if aJSONItem is TPasJSONItemString then
- begin
- result := TPasJSON.StringQuote(TPasJSONItemString(aJSONItem).Value);
- end
- else if aJSONItem is TPasJSONItemArray then
- begin
- result := '[';
- if aFormatting then
- begin
- result := result + #13#10;
- end;
- Count := TPasJSONItemArray(aJSONItem).Count;
- if TPasJSONModeFlag.ImplicitRootObject in aModeFlags then
- begin
- LevelOffset := -1;
- end
- else
- begin
- LevelOffset := 0;
- end;
- for Index := 0 to Count - 1 do
- begin
- if aFormatting then
- begin
- for Ident := 0 to aLevel + LevelOffset do
- begin
- result := result + ' ';
- end;
- end;
- result := result + TPasJSON.Stringify(TPasJSONItemArray(aJSONItem)
- .Items[Index], aFormatting, aModeFlags, aLevel + 1);
- if ((Index + 1) < Count) and not(TPasJSONModeFlag.OptionalCommas
- in aModeFlags) then
- begin
- result := result + ',';
- end;
- if aFormatting then
- begin
- result := result + #13#10;
- end;
- end;
- if aFormatting then
- begin
- for Ident := 1 to aLevel + LevelOffset do
- begin
- result := result + ' ';
- end;
- end;
- result := result + ']';
- end
- else if aJSONItem is TPasJSONItemObject then
- begin
- if (not(TPasJSONModeFlag.ImplicitRootObject in aModeFlags)) or
- (aLevel <> 0) then
- begin
- result := '{';
- if aFormatting then
- begin
- result := result + #13#10;
- end;
- end
- else
- begin
- result := '';
- end;
- if TPasJSONModeFlag.ImplicitRootObject in aModeFlags then
- begin
- LevelOffset := -1;
- end
- else
- begin
- LevelOffset := 0;
- end;
- Count := TPasJSONItemArray(aJSONItem).Count;
- for Index := 0 to Count - 1 do
- begin
- if aFormatting then
- begin
- for Ident := 0 to aLevel + LevelOffset do
- begin
- result := result + ' ';
- end;
- end;
- Key := TPasJSONItemObject(aJSONItem).Keys[Index];
- if (TPasJSONModeFlag.UnquotedKeys in aModeFlags) and IsIdentifier(Key)
- then
- begin
- result := result + TPasJSONRawByteString(Key);
- end
- else
- begin
- result := result + TPasJSON.StringQuote(Key);
- end;
- if TPasJSONModeFlag.EqualsForColon in aModeFlags then
- begin
- if aFormatting then
- begin
- result := result + ' ';
- end;
- result := result + '=';
- end
- else
- begin
- result := result + ':';
- end;
- if aFormatting then
- begin
- result := result + ' ';
- end;
- result := result + TPasJSON.Stringify(TPasJSONItemObject(aJSONItem)
- .Values[Index], aFormatting, aModeFlags, aLevel + 1);
- if ((Index + 1) < Count) and not(TPasJSONModeFlag.OptionalCommas
- in aModeFlags) then
- begin
- result := result + ',';
- end;
- if aFormatting then
- begin
- result := result + #13#10;
- end;
- end;
- if aFormatting then
- begin
- for Ident := 1 to aLevel + LevelOffset do
- begin
- result := result + ' ';
- end;
- end;
- if (not(TPasJSONModeFlag.ImplicitRootObject in aModeFlags)) or
- (aLevel <> 0) then
- begin
- result := result + '}';
- end;
- end
- else
- begin
- result := 'null';
- end;
- end
- else
- begin
- result := 'null';
- end;
- end;
- class procedure TPasJSON.StringifyToStream(const aStream: TStream;
- const aJSONItem: TPasJSONItem; const aFormatting: boolean = false;
- const aModeFlags: TPasJSONModeFlags = []; const aLevel: TPasJSONInt32 = 0);
- var
- StringValue: TPasJSONRawByteString;
- begin
- StringValue := Stringify(aJSONItem, aFormatting, aModeFlags, aLevel);
- try
- if length(StringValue) > 0 then
- begin
- aStream.WriteBuffer(StringValue[1], length(StringValue));
- end;
- finally
- StringValue := '';
- end;
- end;
- class function TPasJSON.GetNumber(const aItem: TPasJSONItem;
- const aDefault: TPasJSONDouble = 0.0): TPasJSONDouble;
- var
- OK: TPasDblStrUtilsBoolean;
- begin
- if assigned(aItem) and (aItem is TPasJSONItemNumber) then
- begin
- result := TPasJSONItemNumber(aItem).Value;
- end
- else if assigned(aItem) and (aItem is TPasJSONItemString) then
- begin
- OK := false;
- result := ConvertStringToDouble(TPasJSONItemString(aItem).Value,
- rmNearest, @OK, -1);
- if not OK then
- begin
- result := aDefault;
- end;
- end
- else if assigned(aItem) and (aItem is TPasJSONItemBoolean) then
- begin
- result := ord(TPasJSONItemBoolean(aItem).Value) and 1;
- end
- else
- begin
- result := aDefault;
- end;
- end;
- class function TPasJSON.GetInt64(const aItem: TPasJSONItem;
- const aDefault: TPasJSONInt64 = 0): TPasJSONInt64;
- begin
- if assigned(aItem) and (aItem is TPasJSONItemNumber) then
- begin
- result := trunc(TPasJSONItemNumber(aItem).Value);
- end
- else if assigned(aItem) and (aItem is TPasJSONItemString) then
- begin
- result := StrToInt64Def(TPasJSONItemString(aItem).Value, aDefault);
- end
- else if assigned(aItem) and (aItem is TPasJSONItemBoolean) then
- begin
- result := ord(TPasJSONItemBoolean(aItem).Value) and 1;
- end
- else
- begin
- result := aDefault;
- end;
- end;
- class function TPasJSON.GetString(const aItem: TPasJSONItem;
- const aDefault: TPasJSONUTF8String = ''): TPasJSONUTF8String;
- begin
- if assigned(aItem) and (aItem is TPasJSONItemString) then
- begin
- result := TPasJSONItemString(aItem).Value;
- end
- else if assigned(aItem) and (aItem is TPasJSONItemNumber) then
- begin
- result := ConvertDoubleToString(TPasJSONItemNumber(aItem).Value,
- omStandard);
- end
- else if assigned(aItem) and (aItem is TPasJSONItemBoolean) then
- begin
- if TPasJSONItemBoolean(aItem).Value then
- begin
- result := 'true';
- end
- else
- begin
- result := 'false';
- end;
- end
- else
- begin
- result := aDefault;
- end;
- end;
- class function TPasJSON.GetBoolean(const aItem: TPasJSONItem;
- const aDefault: boolean = false): boolean;
- begin
- if assigned(aItem) and (aItem is TPasJSONItemBoolean) then
- begin
- result := TPasJSONItemBoolean(aItem).Value;
- end
- else if assigned(aItem) and (aItem is TPasJSONItemNumber) then
- begin
- result := TPasJSONItemNumber(aItem).Value <> 0.0;
- end
- else if assigned(aItem) and (aItem is TPasJSONItemString) then
- begin
- result := (LowerCase(TPasJSONItemString(aItem).Value) <> 'false') and
- (TPasJSONItemString(aItem).Value <> '0');
- end
- else
- begin
- result := aDefault;
- end;
- end;
- class function TPasJSON.LoadBinaryFromStream(const aStream: TStream)
- : TPasJSONItem;
- function LoadJSONItem: TPasJSONItem;
- var
- ItemType, BooleanValue: TPasJSONUInt8;
- Count, Counter, Len: TPasJSONInt32;
- TempString: TPasJSONUTF8String;
- DoubleValue: TPasJSONDouble;
- begin
- result := nil;
- if aStream.Read(ItemType, SizeOf(TPasJSONUInt8)) <> SizeOf(TPasJSONUInt8)
- then
- begin
- raise EInOutError.Create('Stream read error');
- end;
- case ItemType of
- 0:
- begin
- result := TPasJSONItemNull.Create;
- end;
- 1:
- begin
- result := TPasJSONItemArray.Create;
- if aStream.Read(Count, SizeOf(TPasJSONUInt32)) <> SizeOf(TPasJSONInt32)
- then
- begin
- raise EInOutError.Create('Stream read error');
- end;
- for Counter := 0 to Count - 1 do
- begin
- TPasJSONItemArray(result).Add(LoadJSONItem);
- end;
- end;
- 2:
- begin
- TempString := '';
- try
- result := TPasJSONItemObject.Create;
- if aStream.Read(Count, SizeOf(TPasJSONUInt32)) <>
- SizeOf(TPasJSONInt32) then
- begin
- raise EInOutError.Create('Stream read error');
- end;
- for Counter := 0 to Count - 1 do
- begin
- if aStream.Read(Len, SizeOf(TPasJSONUInt32)) <>
- SizeOf(TPasJSONInt32) then
- begin
- raise EInOutError.Create('Stream read error');
- end;
- SetLength(TempString, Len);
- if Len > 0 then
- begin
- if TPasJSONInt64(aStream.Read(TempString[1],
- Len * SizeOf(AnsiChar))) <>
- TPasJSONInt64(Len * SizeOf(AnsiChar)) then
- begin
- raise EInOutError.Create('Stream read error');
- end;
- end;
- TPasJSONItemObject(result).Add(TempString, LoadJSONItem);
- end;
- finally
- TempString := '';
- end;
- end;
- 3:
- begin
- if aStream.Read(BooleanValue, SizeOf(TPasJSONUInt8)) <>
- SizeOf(TPasJSONUInt8) then
- begin
- raise EInOutError.Create('Stream read error');
- end;
- result := TPasJSONItemBoolean.Create(BooleanValue <> 0);
- end;
- 4:
- begin
- if aStream.Read(DoubleValue, SizeOf(TPasJSONDouble)) <>
- SizeOf(TPasJSONDouble) then
- begin
- raise EInOutError.Create('Stream read error');
- end;
- result := TPasJSONItemNumber.Create(DoubleValue);
- end;
- 5:
- begin
- TempString := '';
- try
- if aStream.Read(Len, SizeOf(TPasJSONInt32)) <> SizeOf(TPasJSONInt32)
- then
- begin
- raise EInOutError.Create('Stream read error');
- end;
- SetLength(TempString, Len);
- if Len > 0 then
- begin
- if TPasJSONInt64(aStream.Read(TempString[1],
- Len * SizeOf(AnsiChar))) <> TPasJSONInt64(Len * SizeOf(AnsiChar))
- then
- begin
- raise EInOutError.Create('Stream read error');
- end;
- end;
- result := TPasJSONItemString.Create(TempString);
- finally
- TempString := '';
- end;
- end;
- else
- begin
- raise EInOutError.Create('Stream read error');
- end;
- end;
- end;
- begin
- if assigned(aStream) and (aStream.Position < aStream.Size) then
- begin
- result := LoadJSONItem;
- end
- else
- begin
- result := nil;
- end;
- end;
- class procedure TPasJSON.SaveBinaryToStream(const aStream: TStream;
- const aJSONItem: TPasJSONItem);
- procedure SaveJSONItem(const pCurrentJSONItem: TPasJSONItem);
- var
- ItemType, BooleanValue: TPasJSONUInt8;
- Count, Counter, Len: TPasJSONInt32;
- TempString: TPasJSONUTF8String;
- DoubleValue: TPasJSONDouble;
- begin
- if assigned(pCurrentJSONItem) then
- begin
- if pCurrentJSONItem is TPasJSONItemNull then
- begin
- ItemType := 0;
- if aStream.Write(ItemType, SizeOf(TPasJSONUInt8)) <>
- SizeOf(TPasJSONUInt8) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- end
- else if pCurrentJSONItem is TPasJSONItemArray then
- begin
- ItemType := 1;
- if aStream.Write(ItemType, SizeOf(TPasJSONUInt8)) <>
- SizeOf(TPasJSONUInt8) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- Count := TPasJSONItemArray(pCurrentJSONItem).Count;
- if aStream.Write(Count, SizeOf(TPasJSONInt32)) <> SizeOf(TPasJSONInt32)
- then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- for Counter := 0 to Count - 1 do
- begin
- SaveJSONItem(TPasJSONItemArray(pCurrentJSONItem).Items[Counter]);
- end;
- end
- else if pCurrentJSONItem is TPasJSONItemObject then
- begin
- ItemType := 2;
- if aStream.Write(ItemType, SizeOf(TPasJSONUInt8)) <>
- SizeOf(TPasJSONUInt8) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- Count := TPasJSONItemObject(pCurrentJSONItem).Count;
- if aStream.Write(Count, SizeOf(TPasJSONInt32)) <> SizeOf(TPasJSONInt32)
- then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- try
- for Counter := 0 to Count - 1 do
- begin
- TempString := TPasJSONItemObject(pCurrentJSONItem).Keys[Counter];
- Len := length(TempString);
- if aStream.Write(Len, SizeOf(TPasJSONInt32)) <> SizeOf(TPasJSONInt32)
- then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- if Len > 0 then
- begin
- if TPasJSONInt64(aStream.Write(TempString[1],
- Len * SizeOf(AnsiChar))) <> TPasJSONInt64(Len * SizeOf(AnsiChar))
- then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- end;
- SaveJSONItem(TPasJSONItemObject(pCurrentJSONItem).Values[Counter]);
- end;
- finally
- TempString := '';
- end;
- end
- else if pCurrentJSONItem is TPasJSONItemBoolean then
- begin
- ItemType := 3;
- if aStream.Write(ItemType, SizeOf(TPasJSONUInt8)) <>
- SizeOf(TPasJSONUInt8) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- if TPasJSONItemBoolean(pCurrentJSONItem).fValue then
- begin
- BooleanValue := $FF;
- end
- else
- begin
- BooleanValue := $00;
- end;
- if aStream.Write(BooleanValue, SizeOf(TPasJSONUInt8)) <>
- SizeOf(TPasJSONUInt8) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- end
- else if pCurrentJSONItem is TPasJSONItemNumber then
- begin
- ItemType := 4;
- if aStream.Write(ItemType, SizeOf(TPasJSONUInt8)) <>
- SizeOf(TPasJSONUInt8) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- DoubleValue := TPasJSONItemNumber(pCurrentJSONItem).fValue;
- if aStream.Write(DoubleValue, SizeOf(TPasJSONDouble)) <>
- SizeOf(TPasJSONDouble) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- end
- else if pCurrentJSONItem is TPasJSONItemString then
- begin
- ItemType := 5;
- if aStream.Write(ItemType, SizeOf(TPasJSONUInt8)) <>
- SizeOf(TPasJSONUInt8) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- TempString := TPasJSONItemString(pCurrentJSONItem).fValue;
- Len := length(TempString);
- if aStream.Write(Len, SizeOf(TPasJSONInt32)) <> SizeOf(TPasJSONInt32)
- then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- if Len > 0 then
- begin
- if TPasJSONInt64(aStream.Write(TempString[1], Len * SizeOf(AnsiChar)))
- <> TPasJSONInt64(Len * SizeOf(AnsiChar)) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- end;
- end
- else
- begin
- ItemType := 0;
- if aStream.Write(ItemType, SizeOf(TPasJSONUInt8)) <>
- SizeOf(TPasJSONUInt8) then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- end;
- end
- else
- begin
- ItemType := 0;
- if aStream.Write(ItemType, SizeOf(TPasJSONUInt8)) <> SizeOf(TPasJSONUInt8)
- then
- begin
- raise EInOutError.Create('Stream write error');
- end;
- end;
- end;
- begin
- if assigned(aJSONItem) and assigned(aStream) then
- begin
- SaveJSONItem(aJSONItem);
- end;
- end;
- initialization
- end.
|