// cJSONDictionary class v1.17: // Created by Erik Svensson and published on eriksven.com in April 2013 // License: MIT Define cJSONDICTIONARY_INCLUDED for 1 #IFNDEF cHASHTABLE_INCLUDED Use cHashTable.pkg #ENDIF #IFNDEF cREGEX_INCLUDED Use cRegex.pkg #ENDIF //Data type constants Define _jsond_type for String Define _jsond_unspecified for "unspecified" Define _jsond_null for "null" Define _jsond_string for "string" Define _jsond_number for "number" Define _jsond_object for "object" Define _jsond_array for "array" Define _jsond_object_array for "arrayofobjects" Define _jsond_bool for "boolean" #IFNDEF Is$WebApp #IFNDEF ErrorSystem Use Dferror.pkg #ENDIF Class _cJSONDictionaryErrorSystem is an ErrorSystem Procedure Error_Report Integer ErrNum Integer Err_Line String ErrMsg //Ignore all errors End_Procedure End_Class #ELSE #IFNDEF cWebAppError Use cWebAppError.pkg #ENDIF Class _cJSONDictionaryErrorSystem is an cWebAppError Procedure Error_Report Integer ErrNum Integer Err_Line String ErrMsg //Ignore all errors End_Procedure End_Class #ENDIF //Example: _ARRAY_CAST v String to va #COMMAND _ARRAY_CAST R L "TO" R . !2[] _arr!2 Move !1 to _arr!2 If (SizeOfArray(_arr!2) > 0) Begin Move (ResizeArray(!4,0)) to !4 For i from 0 to (SizeOfArray(_arr!2)-1) #IFSAME !2 Number Decimal Real Move (Cast(_arr!2[i], Real)) to !4[i] #ELSE Move _arr!2[i] to !4[i] #ENDIF Loop End #ENDCOMMAND Class cJSONDictionary is a cHashTable Procedure Construct_Object Forward Send Construct_Object End_Procedure { Visibility=Public } Function Is_cJSONDictionary Returns Boolean Function_Return True End_Function Function _ValueKey String sKey Returns String Function_Return ("__[internal]__[t]*" + sKey) End_Function Function _IsValueKey String sKey Returns Boolean Function_Return (Pos("__[internal]__[t]",sKey)) End_Function Function _IntToHex Integer iVal Returns String Integer iRest String sVal sChar sBase Move "0123456789ABCDEF" to sBase Repeat Move (Mod(iVal,16)) to iRest Move (mid(sBase,1,iRest+1)) to sChar Move (sChar+sVal) to sVal Move (iVal/16) to iVal Until (iVal=0) Function_Return sVal End_Function Procedure _Escape String ByRef s Integer i If (s = NULL_ENTRY) Begin Move "" to s Procedure_Return End Move (Replaces('\',s,'\\')) to s Move (Replaces('\\u',s,'\u')) to s Move (Replaces('/',s,'\/')) to s Move (Replaces('"',s,'\"')) to s For i from 1 to 31 If (i <= 15) Begin Move (Replaces(Character(i), s, ("\u000" + _IntToHex(Self, i)))) to s End Else Begin Move (Replaces(Character(i), s, ("\u00" + _IntToHex(Self, i)))) to s End Loop End_Procedure Function _ArrayCast Variant v Returns Variant[] Variant[] va Integer i _ARRAY_CAST v Address to va _ARRAY_CAST v Integer to va _ARRAY_CAST v BigInt to va _ARRAY_CAST v Boolean to va _ARRAY_CAST v Char to va _ARRAY_CAST v Currency to va _ARRAY_CAST v Date to va _ARRAY_CAST v DateTime to va _ARRAY_CAST v Decimal to va _ARRAY_CAST v Float to va _ARRAY_CAST v Number to va _ARRAY_CAST v Real to va _ARRAY_CAST v RowID to va _ARRAY_CAST v Short to va _ARRAY_CAST v String to va _ARRAY_CAST v Time to va _ARRAY_CAST v TimeSpan to va _ARRAY_CAST v UBigInt to va _ARRAY_CAST v UChar to va _ARRAY_CAST v UInteger to va _ARRAY_CAST v UShort to va _ARRAY_CAST v Variant to va Function_Return va End_Function Function _ValueTypeCheck Variant v _jsond_type match Returns Boolean Case Begin Case (match = _jsond_null) Function_Return (v = Character(0)) Case Break Case (match = _jsond_string) String s Move (Number(v)) to s Function_Return (String(v) <> s) Case Break Case (match = _jsond_number) Function_Return (Regex_IsMatch(v, "^(-)?(\d+)((.|,)\d+)?$")) Case Break Case End End_Function Function _Type String sKey Variant v Returns _jsond_type Variant[] va _jsond_type type Get TypeForValue sKey to type If (type <> _jsond_unspecified) Function_Return type Get _ArrayCast v to va If (SizeOfArray(va) > 0) Function_Return _jsond_array If (Is_cJSONDictionary(v)) Function_Return _jsond_object If (_ValueTypeCheck(Self, v, _jsond_string)) Function_Return _jsond_string If (_ValueTypeCheck(Self, v, _jsond_number)) Function_Return _jsond_number If (_ValueTypeCheck(Self, v, _jsond_null)) Function_Return _jsond_null End_Function Function _JSONNumber String s Returns String Integer iDSep ITSep Get_Attribute DF_DECIMAL_SEPARATOR to iDSep Get_Attribute DF_THOUSANDS_SEPARATOR to ITSep Function_Return (Replace(Character(ITSep), Replace(Character(iDSep), s, '.'), '')) End_Function Procedure _Serialize String[] saKeys String ByRef sJSON Integer i ii iLastKey iLastElement Variant v Boolean b Variant[] va String sKey sValue sChildValue _jsond_type type child_type Append sJSON '{' Move (SizeOfArray(saKeys)-1) to iLastKey For i from 0 to iLastKey Get Value saKeys[i] to v Get _Type saKeys[i] v to type Move saKeys[i] to sKey Send _Escape (&sKey) Case Begin Case (type = _jsond_null) Move 'null' to sValue Case Break Case (type = _jsond_bool) Move v to b Move (If(b, "true", "false")) to sValue Case Break Case (type = _jsond_string or type = _jsond_unspecified) Move v to sValue Send _Escape (&sValue) Move ('"' + sValue + '"') to sValue Case Break Case (type = _jsond_number) Move (_JSONNumber(Self, v)) to sValue Case Break Case (type = _jsond_object) Get Serialize of v to sValue Case Break Case (type = _jsond_array) Get _ArrayCast v to va Move (SizeOfArray(va)-1) to iLastElement Move "[" to sValue For ii from 0 to iLastElement Get _Type "" va[ii] to child_type If (child_type = _jsond_null) Begin Append sValue ("null" + If(ii < iLastElement,',','')) End Else If (child_type = _jsond_bool) Begin Move va[ii] to b Append sValue (If(ii < iLastElement,',','') + If(b, "true", "false")) End Else If (child_type = _jsond_string or child_type = _jsond_unspecified) Begin Move va[ii] to sChildValue Send _Escape (&sChildValue) Append sValue ('"' + sChildValue + '"' + If(ii < iLastElement,',','')) End Else If (child_type = _jsond_number) Begin Move va[ii] to sChildValue Append sValue (_JSONNumber(Self, sChildValue) + If(ii < iLastElement,',','')) End Else If (child_type = _jsond_object) Begin Get Serialize of va[ii] to sChildValue Append sValue (sChildValue + If(ii < iLastElement,',','')) End Loop Append sValue "]" Case Break Case (type = _jsond_object_array) Get _ArrayCast v to va Move (SizeOfArray(va)-1) to iLastElement Move "[" to sValue For ii from 0 to iLastElement If (va[ii] <= 0) Move "{}" to sChildValue else Get Serialize of va[ii] to sChildValue Append sValue (sChildValue + If(ii < iLastElement,',','')) Loop Append sValue "]" Case Break Case End Append sJSON ('"' + sKey + '":' + sValue + If(i < iLastKey,',','')) Loop Append sJSON '}' End_Procedure { Visibility=Public } Procedure Set MicrosoftDateValue String sKey DateTime dt TimeSpan ts Real ms DateTime dt1970 If (IsNullDateTime(dt) or DateGetYear(dt) < 1970) Begin Set Value sKey to "/Date(0)/" End Else Begin Move (DateSetYear(dt1970, 1970)) to dt1970 Move (DateSetMonth(dt1970, 1)) to dt1970 Move (DateSetDay(dt1970, 1)) to dt1970 Move (dt - dt1970) to ts Move (SpanTotalMilliseconds(ts)) to ms Set Value sKey to ("/Date(" + String(ms) + ")/") End End_Procedure { Visibility=Public } Procedure Set TypedValue String sKey _jsond_type type Variant vValue String sValueKey Forward Set Value sKey to vValue If (type = _jsond_unspecified) Procedure_Return Get _ValueKey sKey to sValueKey Set Value sValueKey to type End_Procedure { Visibility=Public } Procedure Set StringValue String sKey String sValue Set TypedValue sKey _jsond_string to sValue End_Procedure { Visibility=Public } Function StringValue String sKey Returns String String sRetVal Get Value sKey to sRetVal Function_Return sRetVal End_Function { Visibility=Public } Procedure Set NumericValue String sKey Number nValue Set TypedValue sKey _jsond_number to nValue End_Procedure { Visibility=Public } Function NumericValue String sKey Returns Number Number nRetVal Get Value sKey to nRetVal Function_Return nRetVal End_Function { Visibility=Public } Procedure Set BooleanValue String sKey Boolean bValue Set TypedValue sKey _jsond_bool to bValue End_Procedure { Visibility=Public } Function BooleanValue String sKey Returns Boolean Boolean bRetVal Get Value sKey to bRetVal Function_Return bRetVal End_Function { Visibility=Public } Procedure Set ArrayValue String sKey Variant vArray Set TypedValue sKey _jsond_array to vArray End_Procedure { Visibility=Public } Function ArrayValue String sKey Returns Variant Variant vRetVal Get Value sKey to vRetVal Function_Return vRetVal End_Function { Visibility=Public } Procedure Set ObjectValue String sKey Handle hJsonDictionary Set TypedValue sKey _jsond_object to hJsonDictionary End_Procedure { Visibility=Public } Function ObjectValue String sKey Returns Handle Handle hRetVal Get Value sKey to hRetVal Function_Return hRetVal End_Function { Visibility=Public } Procedure Set ObjectArrayValue String sKey Handle[] haJsonDictionaries Set TypedValue sKey _jsond_object_array to haJsonDictionaries End_Procedure { Visibility=Public } Function ObjectArrayValue String sKey Returns Handle[] Handle[] haRetVal Get Value sKey to haRetVal Function_Return haRetVal End_Function { Visibility=Public } Function TypeForValue String sKey Returns _jsond_type _jsond_type type Get Value (_ValueKey(Self, sKey)) to type If (not(Found)) Function_Return _jsond_unspecified Function_Return type End_Function { Visibility=Public } Function ValueAtPath String sPath Returns Variant tRegexMatch[] keys tRegexMatch key Integer i iMax iIdx Variant v Variant[] va String sArrKey Get Regex_Match sPath "[^\.]+(?=(\.|$))" True to keys Move (Self) to v Move (SizeOfArray(keys)-1) to iMax For i from 0 to iMax Send Ignore_Error of Error_Object_Id DFERR_BAD_SYMBOL_EXPRESSION Send Ignore_Error of Error_Object_Id DFERR_ILLEGAL_CONVERSION Send Ignore_Error of Error_Object_Id DFERR_ARRAY_INDEX_OUT_OF_BOUNDS Move False to Err Move keys[i] to key If (Regex_IsMatch(key.Val, "\[\d+\]")) Begin Move (Regex_Replace(key.Val, "", "\[\d+\]", True)) to sArrKey Move (Regex_Replace(key.Val, "", ".*\[|\]", True)) to iIdx Get Value of v sArrKey to v If (not(Found)) Function_Return NULL_ENTRY Move v to va Move va[iIdx] to v End Else Begin Get Value of v key.Val to v If (not(Found)) Function_Return NULL_ENTRY End Send Trap_Error of Error_Object_Id DFERR_BAD_SYMBOL_EXPRESSION Send Trap_Error of Error_Object_Id DFERR_ILLEGAL_CONVERSION Send Trap_Error of Error_Object_Id DFERR_ARRAY_INDEX_OUT_OF_BOUNDS If (Err) Begin Move False to Found Function_Return NULL_ENTRY End Loop Move True to Found Function_Return v End_Function Function Keys Returns String[] String[] sa saFiltered Integer i iFilteredIdx iLastKey Forward Get Keys to sa Move (SizeOfArray(sa)-1) to iLastKey For i from 0 to iLastKey If (not(_IsValueKey(Self, sa[i]))) Begin Move sa[i] to saFiltered[iFilteredIdx] Increment iFilteredIdx End Loop Function_Return saFiltered End_Function { Visibility=Public } Function Serialize Returns String String sJSON Handle hPreviousErrorSystem Move Error_Object_Id to hPreviousErrorSystem Move (Create(Self,U__cJSONDictionaryErrorSystem)) to Error_Object_Id Move "" to sJSON Send _Serialize (Keys(Self)) (&sJSON) Send Destroy of Error_Object_Id Move hPreviousErrorSystem to Error_Object_Id Function_Return sJSON End_Function End_Class