├── JSON-Test.au3 ├── JSON.au3 ├── JSON_Dictionary.au3 ├── LICENSE ├── README.md ├── example.json └── example.min.json /JSON-Test.au3: -------------------------------------------------------------------------------- 1 | #include "JSON.au3" 2 | Global $s_String = '[{"id":"4434156","url":"https://legacy.sky.com/v2/schedules/4434156","title":"468_CORE_1_R.4 Schedule","time_zone":"London","start_at":"2017/08/10 19:00:00 +0100","end_at":null,"notify_user":false,"delete_at_end":false,"executions":[],"recurring_days":[],"actions":[{"type":"run","offset":0}],"next_action_name":"run","next_action_time":"2017/08/10 14:00:00 -0400","user":{"id":"9604","url":"https://legacy.sky.com/v2/users/9604","login_name":"robin@ltree.com","first_name":"Robin","last_name":"John","email":"robin@ltree.com","role":"admin","deleted":false},"region":"EMEA","can_edit":true,"vm_ids":null,"configuration_id":"19019196","configuration_url":"https://legacy.sky.com/v2/configurations/19019196","configuration_name":"468_CORE_1_R.4"},{"id":"4444568","url":"https://legacy.sky.com/v2/schedules/4444568","title":"468_CORE_1_R.4 Schedule","time_zone":"London","start_at":"2017/08/11 12:00:00 +0100","end_at":null,"notify_user":false,"delete_at_end":false,"executions":[],"recurring_days":[],"actions":[{"type":"suspend","offset":0}],"next_action_name":"suspend","next_action_time":"2017/08/11 07:00:00 -0400","user":{"id":"9604","url":"https://legacy.sky.com/v2/users/9604","login_name":"robin@ltree.com","first_name":"Robin","last_name":"John","email":"robin@ltree.com","role":"admin","deleted":false},"region":"EMEA","can_edit":true,"vm_ids":null,"configuration_id":"19019196","configuration_url":"https://legacy.sky.com/v2/configurations/19019196","configuration_name":"468_CORE_1_R.4"}]' 3 | 4 | ; ================= parse the JSON-String into a nested AutoIt data structure ============== 5 | $o_Object = _JSON_Parse($s_String) 6 | 7 | ; ================= query values from the structure directly with AutoIt syntax ============ 8 | $s_Type = ((($o_Object[1])["actions"])[0])["type"] 9 | ConsoleWrite("type: " & $s_Type & @CRLF) 10 | 11 | ; ================= query values via _JSON_Get() (safer and clearer) ======================= 12 | $s_Type = _JSON_Get($o_Object, "[1].actions[0].type") 13 | ConsoleWrite("type: " & $s_Type & @CRLF) 14 | 15 | ; ================= convert AutoIt data structures into a JSON string ====================== 16 | ConsoleWrite(_JSON_Generate($o_Object) & @CRLF & @CRLF) 17 | ; compact form: 18 | ConsoleWrite(_JSON_GenerateCompact($o_Object) & @CRLF & @CRLF) 19 | 20 | ; ================= convert minified JSON file to unminified JSON string ===== 21 | $s_File = 'example.min.json' 22 | ConsoleWrite(_JSON_Unminify($s_File) & @CRLF & @CRLF) 23 | 24 | ; ================= convert unminified JSON file to minified JSON string ===== 25 | $s_File = 'example.json' 26 | ConsoleWrite(_JSON_Minify($s_File) & @CRLF) 27 | -------------------------------------------------------------------------------- /JSON.au3: -------------------------------------------------------------------------------- 1 | #include-once 2 | 3 | ; #INDEX# ======================================================================================================================= 4 | ; Title .........: JSON-UDF 5 | ; Version .......: 0.4 6 | ; AutoIt Version : 3.3.16.1 7 | ; Language ......: english (german maybe by accident) 8 | ; Description ...: Function for interacting with JSON data in AutoIt. 9 | ; This includes import, export as well as helper functions for handling nested AutoIt data structures. 10 | ; Author(s) .....: AspirinJunkie, Sven Seyfert (SOLVE-SMART) 11 | ; Last changed ..: 2025-08-13 12 | ; Link ..........: https://autoit.de/thread/85435-json-udf/ 13 | ; License .......: This work is free. 14 | ; You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, 15 | ; as published by Sam Hocevar. 16 | ; See http://www.wtfpl.net/ for more details. 17 | ; =============================================================================================================================== 18 | 19 | ; #Function list# ======================================================================================================================= 20 | ; ---- import and export from or to json ------ 21 | ; _JSON_Parse - converts a JSON-structured string into a nested AutoIt data structure 22 | ; _JSON_Generate - converts a nested AutoIt data structure into a JSON structured string 23 | ; _JSON_GenerateCompact - shorthand for _JSON_Generate() to create JSON structured strings as compact as possible 24 | ; _JSON_Unminify - reads minified (compact) JSON file or string and converts to well readable JSON string 25 | ; _JSON_Minify - reads unminified (readable) JSON file or string and converts to minified (compact) JSON string 26 | ; 27 | ; ---- extraction and manipulation of nested AutoIt data structures ---- 28 | ; _JSON_Get - extract query nested AutoIt-datastructure with a simple selector string 29 | ; _JSON_addChangeDelete - create a nested AutoIt data structure, change values within existing structures or delete elements from a nested AutoIt data structure 30 | ; 31 | ; ---- helper functions ---- 32 | ; __JSON_FormatString - converts a string into a json string by escaping the special symbols 33 | ; __JSON_ParseString - converts a json formatted string into an AutoIt-string by unescaping the json-escapes 34 | ; __JSON_A2DToAinA - converts a 2D array into a Arrays in Array 35 | ; __JSON_AinAToA2d - converts a Arrays in Array into a 2D array 36 | ; __JSON_Base64Decode - decode data which is coded as a base64-string into binary variable 37 | ; __JSON_Base64Encode - converts a binary- or string-Input into BASE64 (or optional base64url) format 38 | ; =============================================================================================================================== 39 | 40 | ; #FUNCTION# ====================================================================================== 41 | ; Name ..........: _JSON_Parse 42 | ; Description ...: convert a JSON-formatted string into a nested structure of AutoIt-datatypes 43 | ; Syntax ........: _JSON_Parse(Const $sString, $iOs = 1) 44 | ; Parameters ....: $sString - a string formatted as JSON 45 | ; [$iOs] - search position where to start (normally don't touch!) 46 | ; Return values .: Success - Return a nested structure of AutoIt-datatypes 47 | ; @extended = next string offset 48 | ; Failure - Return "" and set @error to: 49 | ; @error = 1 - part is not json-syntax 50 | ; = 2 - key name in object part is not json-syntax 51 | ; = 3 - value in object is not correct json 52 | ; = 4 - delimiter or object end expected but not gained 53 | ; Author ........: AspirinJunkie 54 | ; ================================================================================================= 55 | Func _JSON_Parse(Const $sString, $iOs = 1) 56 | Local $vValue 57 | Local Static $sRE_JSONElements = '\G\s*(?>' & _ 58 | '"([^"\\]*+(?>\\.[^"\\]*+)*)"|' & _ ; String (group 1) 59 | '(-?(?>0|[1-9]\d*)(?>\.\d+)?(?>[eE][-+]?\d+)?)|' & _ ; Number (group 2) 60 | '(\{)|' & _ ; Object begin (group 3) 61 | '(\[)|' & _ ; Array begin (group 4) 62 | '\b(null|true|false)\b)', _ ; Keywords (group 5) 63 | $sRE_Object_Key = '\G\s*"([^"\\]*+(?>\\.[^"\\]*+)*)"\s*:', _ ; the key of an object 64 | $sRE_Object_Further = '\G\s*,', _ ; object element separator 65 | $sRE_Object_End = '\G\s*\}', _ ; object end 66 | $sRE_Array_End = '\G\s*\]' ; array end 67 | 68 | Local $aMatch = StringRegExp($sString, $sRE_JSONElements, 1, $iOs) 69 | If @error Then Return SetError(1, $iOs, "") 70 | $iOs = @extended 71 | 72 | ; Determine type of current element and process accordingly 73 | Switch UBound($aMatch) 74 | Case 1 ; String 75 | Return SetExtended($iOs, StringInStr($aMatch[0], "\", 1) ? __JSON_ParseString($aMatch[0]) : $aMatch[0]) 76 | Case 2 ; Number 77 | Return SetExtended($iOs, Number($aMatch[1])) 78 | Case 3 ; Object begin 79 | Local $oCurrent[], $aKey 80 | 81 | ; Check for empty object 82 | StringRegExp($sString, $sRE_Object_End, 1, $iOs) 83 | If Not @error Then Return SetExtended(@extended, $oCurrent) 84 | 85 | Do 86 | ; extract the element key 87 | $aKey = StringRegExp($sString, $sRE_Object_Key, 1, $iOs) 88 | If @error Then Return SetError(2, $iOs, "") 89 | $iOs = @extended 90 | 91 | ; extract the element value 92 | $vValue = _JSON_Parse($sString, $iOs) 93 | If @error Then Return SetError(3, $iOs, "") 94 | $iOs = @extended 95 | 96 | ; add current element value to map linked to the current element key 97 | $oCurrent[(StringInStr($aKey[0], "\", 1) ? __JSON_ParseString($aKey[0]) : $aKey[0])] = $vValue 98 | 99 | ; check for further elements in the object 100 | StringRegExp($sString, $sRE_Object_Further, 1, $iOs) ; more elements 101 | If Not @error Then 102 | $iOs = @extended 103 | ContinueLoop 104 | Else 105 | StringRegExp($sString, $sRE_Object_End, 1, $iOs) 106 | If Not @error Then ; end of object reached 107 | Return SetExtended(@extended, $oCurrent) 108 | Else ; syntax error 109 | Return SetError(4, $iOs, "") 110 | EndIf 111 | EndIf 112 | Until False 113 | Case 4 ; Array begin 114 | ; Start with larger initial array size for better performance 115 | Local $aCurrent[16], $iSize = 16, $iCount = 0 116 | 117 | ; Check for empty array 118 | StringRegExp($sString, $sRE_Array_End, 1, $iOs) 119 | If Not @error Then 120 | ReDim $aCurrent[0] 121 | Return SetExtended(@extended, $aCurrent) 122 | EndIf 123 | 124 | Do 125 | $vValue = _JSON_Parse($sString, $iOs) 126 | If @error Then Return SetError(3, $iOs, "") 127 | $iOs = @extended 128 | 129 | ; array resize if necessary 130 | If $iCount >= $iSize Then 131 | $iSize += $iSize 132 | ReDim $aCurrent[$iSize] 133 | EndIf 134 | $aCurrent[$iCount] = $vValue 135 | $iCount += 1 136 | 137 | ; check for further elements in the array 138 | StringRegExp($sString, $sRE_Object_Further, 1, $iOs) 139 | If Not @error Then ; more elements found 140 | $iOs = @extended 141 | ContinueLoop 142 | Else 143 | StringRegExp($sString, $sRE_Array_End, 1, $iOs) ; end of array reached 144 | If Not @error Then 145 | If $iCount <> $iSize Then ReDim $aCurrent[$iCount] 146 | Return SetExtended(@extended, $aCurrent) 147 | Else ; syntax error 148 | Return SetError(5, $iOs, "") 149 | EndIf 150 | EndIf 151 | Until False 152 | Case 5 ; Keywords 153 | Return SetExtended($iOs, Execute($aMatch[4])) 154 | EndSwitch 155 | 156 | Return SetError(1, $iOs, "") 157 | EndFunc ;==>_JSON_Parse 158 | 159 | ; #FUNCTION# ====================================================================================== 160 | ; Name ..........: _JSON_Generate 161 | ; Description ...: converts a nested AutoIt data structure into a JSON structured string 162 | ; Syntax ........: _JSON_Generate($oObject, $sObjIndent = @TAB, $sObjDelEl = @CRLF, $sObjDelKey = " ", $sObjDelVal = "", $sArrIndent = @TAB, $sArrDelEl = @CRLF, $iLevel = 0) 163 | ; Parameters ....: $oObject - [nested] AutoIt data structure 164 | ; [$sObjIndent] - indent for object elements (only reasonable if $sObjDelEl contains a line skip 165 | ; [$sObjDelEl] - delimiter between object elements 166 | ; [$sObjDelKey] - delimiter between keyname and ":" in object 167 | ; [$sObjDelVal] - delimiter between ":" and value in object 168 | ; [$sArrIndent] - indent for array elements (only reasonable if $sArrDelEl contains a line skip) 169 | ; [$sArrDelEl] - delimiter between array elements 170 | ; [$iLevel] - search position where to start (normally don't touch!) 171 | ; Return values .: Success - Return a JSON formatted string 172 | ; Failure - Return "" 173 | ; Author ........: AspirinJunkie 174 | ; ================================================================================================= 175 | Func _JSON_Generate($oObject, $sObjIndent = @TAB, $sObjDelEl = @CRLF, $sObjDelKey = "", $sObjDelVal = " ", $sArrIndent = @TAB, $sArrDelEl = @CRLF, $iLevel = 0) 176 | Local Static $sJSON_String 177 | Local Static $aObjIndCache[0], $aArrIndCache[0] 178 | Local $sKeyTemp, $oValue 179 | If $iLevel = 0 Then 180 | $sJSON_String = "" 181 | Redim $aObjIndCache[20] 182 | Redim $aArrIndCache[20] 183 | $aObjIndCache[1] = $sObjIndent 184 | $aArrIndCache[1] = $sArrIndent 185 | EndIf 186 | 187 | ; Cache to avoid StringRepeat 188 | If UBound($aObjIndCache) < $iLevel + 2 Then ; for structures with large amount of levels 189 | Redim $aObjIndCache[$iLevel + 2] 190 | Redim $aArrIndCache[$iLevel + 2] 191 | EndIf 192 | ; calculate the current indentation 193 | If $aArrIndCache[$iLevel + 1] = "" And $sArrIndent <> "" Then $aArrIndCache[$iLevel + 1] = $aArrIndCache[$iLevel] & $sArrIndent 194 | If $aObjIndCache[$iLevel + 1] = "" And $sObjIndent <> "" Then $aObjIndCache[$iLevel + 1] = $aObjIndCache[$iLevel] & $sObjIndent 195 | 196 | Switch VarGetType($oObject) 197 | Case "String" 198 | $sJSON_String &= '"' & __JSON_FormatString($oObject) & '"' 199 | Case "Int32", "Int64", "Float", "Double" 200 | $sJSON_String &= String($oObject) 201 | Case "Bool" 202 | $sJSON_String &= StringLower($oObject) 203 | Case "Keyword" 204 | If IsKeyword($oObject) = 2 Then $sJSON_String &= "null" 205 | Case "Binary" 206 | $sJSON_String &= '"' & __JSON_Base64Encode($oObject) & '"' 207 | Case "Array" 208 | If UBound($oObject, 0) = 2 Then $oObject = __JSON_A2DToAinA($oObject) 209 | If UBound($oObject) = 0 Then 210 | $sJSON_String &= "[]" 211 | Else 212 | $sJSON_String &= "[" & $sArrDelEl 213 | For $oValue In $oObject 214 | $sJSON_String &= $aArrIndCache[$iLevel + 1] 215 | _JSON_Generate($oValue, $sObjIndent, $sObjDelEl, $sObjDelKey, $sObjDelVal, $sArrIndent, $sArrDelEl, $iLevel + 1) 216 | 217 | $sJSON_String &= "," & $sArrDelEl 218 | Next 219 | $sJSON_String = StringTrimRight($sJSON_String, StringLen("," & $sArrDelEl)) & $sArrDelEl & $aArrIndCache[$iLevel] & "]" 220 | EndIf 221 | Case "Object" 222 | If ObjName($oObject) = "Dictionary" Then 223 | If $oObject.Count() = 0 Then 224 | $sJSON_String &= "{}" 225 | Else 226 | $sJSON_String &= "{" & $sObjDelEl 227 | For $sKey In $oObject.Keys 228 | $sKeyTemp = $sKey 229 | $oValue = $oObject($sKey) 230 | 231 | $sJSON_String &= $aObjIndCache[$iLevel + 1] & '"' & __JSON_FormatString($sKeyTemp) & '"' & $sObjDelKey & ':' & $sObjDelVal 232 | 233 | _JSON_Generate($oValue, $sObjIndent, $sObjDelEl, $sObjDelKey, $sObjDelVal, $sArrIndent, $sArrDelEl, $iLevel + 1) 234 | 235 | $sJSON_String &= "," & $sObjDelEl 236 | Next 237 | $sJSON_String = StringTrimRight($sJSON_String, StringLen("," & $sObjDelEl)) & $sObjDelEl & $aObjIndCache[$iLevel] & "}" 238 | EndIf 239 | EndIf 240 | Case "Map" 241 | If UBound($oObject) = 0 Then 242 | $sJSON_String &= "{}" 243 | Else 244 | $sJSON_String &= "{" & $sObjDelEl 245 | For $sKey In MapKeys($oObject) 246 | $sKeyTemp = $sKey 247 | $oValue = $oObject[$sKey] 248 | 249 | $sJSON_String &= $aObjIndCache[$iLevel + 1] & '"' & __JSON_FormatString($sKeyTemp) & '"' & $sObjDelKey & ':' & $sObjDelVal 250 | 251 | _JSON_Generate($oValue, $sObjIndent, $sObjDelEl, $sObjDelKey, $sObjDelVal, $sArrIndent, $sArrDelEl, $iLevel + 1) 252 | 253 | $sJSON_String &= "," & $sObjDelEl 254 | Next 255 | $sJSON_String = StringTrimRight($sJSON_String, StringLen("," & $sObjDelEl)) & $sObjDelEl & $aObjIndCache[$iLevel] & "}" 256 | EndIf 257 | Case "Ptr" 258 | $sJSON_String &= String(Int($oObject)) 259 | Case "DLLStruct" 260 | Local $tBin = DllStructCreate("Byte[" & DllStructGetSize($oObject) & "]", DllStructGetPtr($oObject)) 261 | $sJSON_String &= '"' & __JSON_Base64Encode(DllStructGetData($tBin, 1)) & '"' 262 | EndSwitch 263 | 264 | If $iLevel = 0 Then 265 | Local $sTemp = $sJSON_String 266 | $sJSON_String = "" 267 | 268 | ; clear indent cache: 269 | Redim $aObjIndCache[0] 270 | Redim $aArrIndCache[0] 271 | 272 | Return $sTemp 273 | EndIf 274 | EndFunc ;==>_JSON_Generate 275 | 276 | ; #FUNCTION# ==================================================================================================================== 277 | ; Name ..........: _JSON_GenerateCompact 278 | ; Description ...: shorthand for _JSON_Generate()-parameters to produce a compact as possible JSON string 279 | ; Syntax ........: _JSON_GenerateCompact($oObject) 280 | ; Parameters ....: $oObject - [nested] AutoIt data structure 281 | ; Return values .: Success - Return a JSON formatted string 282 | ; Failure - Return "" 283 | ; Author ........: AspirinJunkie 284 | ; Modified ......: 2023-05-23 285 | ; Related .......: _JSON_Generate 286 | ; =============================================================================================================================== 287 | Func _JSON_GenerateCompact($oObject) 288 | Return _JSON_Generate($oObject, "", "", "", "", "", "") 289 | EndFunc ;==>_JSON_GenerateCompact 290 | 291 | ; #FUNCTION# ==================================================================================================================== 292 | ; Name ..........: _JSON_Unminify 293 | ; Description ...: reads minified (compact) JSON file or string and converts to well readable JSON string 294 | ; Syntax ........: _JSON_Unminify($sInput) 295 | ; Parameters ....: $sInput - json file path/handle or json string 296 | ; Return values .: Success - Return a JSON formatted string 297 | ; Failure - Return "" and set @error to: 298 | ; @error = 1 - error during FileRead() - @extended = @error from FileRead() 299 | ; = 2 - no valid format for $sInput 300 | ; Author ........: Sven Seyfert (SOLVE-SMART), AspirinJunkie 301 | ; Related .......: _JSON_Generate 302 | ; =============================================================================================================================== 303 | Func _JSON_Unminify($sInput) 304 | ; read file if $sInput = file name or file handle 305 | If FileExists($sInput) Or IsInt($sInput) Then $sInput = FileRead($sInput) 306 | If @error Then Return SetError(1, @error, False) 307 | If Not IsString($sInput) Then Return SetError(2, 0, False) 308 | 309 | Local Const $oObject = _JSON_Parse($sInput) 310 | Return _JSON_Generate($oObject) 311 | EndFunc ;==>_JSON_Unminify 312 | 313 | ; #FUNCTION# ==================================================================================================================== 314 | ; Name ..........: _JSON_Minify 315 | ; Description ...: reads unminified (readable) JSON file or string and converts to minified (compact) JSON string 316 | ; Syntax ........: _JSON_Minify($sInput) 317 | ; Parameters ....: $sInput - json file path/handle or json string 318 | ; Return values .: Success - Return a JSON formatted string 319 | ; Failure - Return "" and set @error to: 320 | ; @error = 1 - error during FileRead() - @extended = @error from FileRead() 321 | ; = 2 - no valid format for $sInput 322 | ; Author ........: Sven Seyfert (SOLVE-SMART), AspirinJunkie 323 | ; Related .......: _JSON_GenerateCompact 324 | ; =============================================================================================================================== 325 | Func _JSON_Minify($sInput) 326 | ; read file if $sInput = file name or file handle 327 | If FileExists($sInput) Or IsInt($sInput) Then $sInput = FileRead($sInput) 328 | If @error Then Return SetError(1, @error, False) 329 | If Not IsString($sInput) Then Return SetError(2, 0, False) 330 | 331 | Local Const $oObject = _JSON_Parse($sInput) 332 | Return _JSON_GenerateCompact($oObject) 333 | EndFunc ;==>_JSON_Minify 334 | 335 | ; #FUNCTION# ====================================================================================== 336 | ; Name ..........: _JSON_Get 337 | ; Description ...: query nested AutoIt-datastructure with a simple query string with syntax: 338 | ; MapKey#1.MapKey#2.[ArrayIndex#1].MapKey#3... (points keynames can be achieved by "\.") 339 | ; multidimensional (2D or 3D only) array indices are separated through comma - e.g.: [2,3] 340 | ; Syntax ........: _JSON_Get(ByRef $oObject, Const $sPattern) 341 | ; Parameters ....: $oObject - a nested AutoIt datastructure (Arrays, Dictionaries, basic scalar types) 342 | ; $sPattern - query pattern like described above 343 | ; Return values .: Success - Return the queried object out of the nested datastructure 344 | ; Failure - Return "" and set @error to: 345 | ; @error = 1 - pattern is not correct 346 | ; = 2 - keyname query to none dictionary object 347 | ; = 3 - keyname queried not exists in dictionary 348 | ; = 4 - index query on none array object 349 | ; = 5 - index out of array range 350 | ; = 6 - number of subindices in index query not match array dimensions 351 | ; = 7 - more than 3 array dimensions are not supported 352 | ; Author ........: AspirinJunkie 353 | ; ================================================================================================= 354 | Func _JSON_Get(ByRef $oObject, Const $sPattern) 355 | Local $oCurrent = $oObject, $d_Val 356 | Local $aTokens = StringRegExp($sPattern, '\[(\d+|[\d\h,]+)\]|((?>\\.|[^\.\[\]\\]+)+)', 4) 357 | If @error Then Return SetError(1, @error, "") 358 | 359 | For $aCurToken In $aTokens 360 | 361 | If UBound($aCurToken) = 3 Then ; KeyName 362 | $aCurToken[2] = StringRegExpReplace($aCurToken[2], '\\(.)', '$1') 363 | Switch VarGetType($oCurrent) 364 | Case "Object" 365 | If Not IsObj($oCurrent) Or ObjName($oCurrent) <> "Dictionary" Then Return SetError(2, 0, "") 366 | If Not $oCurrent.Exists($aCurToken[2]) Then Return SetError(3, 0, "") 367 | 368 | $oCurrent = $oCurrent($aCurToken[2]) 369 | Case "Map" 370 | If Not MapExists($oCurrent, $aCurToken[2]) Then Return SetError(3, 0, "") 371 | 372 | $oCurrent = $oCurrent[$aCurToken[2]] 373 | EndSwitch 374 | ElseIf UBound($aCurToken) = 2 Then ; ArrayIndex 375 | If (Not IsArray($oCurrent)) Then Return SetError(4, UBound($oCurrent, 0), "") 376 | 377 | ; multi dimensional array 378 | If StringInStr($aCurToken[1], ',', 1) Then 379 | Local $aIndices = StringSplit($aCurToken[1], ',', 3) 380 | If UBound($aIndices) <> UBound($oCurrent, 0) Then Return SetError(6, UBound($oCurrent, 0), "") 381 | 382 | ; get the indices and check their range 383 | Local $x = Int($aIndices[0]), $y = Int($aIndices[1]) 384 | If $x < 0 Or $x >= UBound($oCurrent, 1) Then Return SetError(5, $x, "") 385 | If $y < 0 Or $y >= UBound($oCurrent, 2) Then Return SetError(5, $y, "") 386 | Switch UBound($aIndices) 387 | Case 2 ; 2D array 388 | $oCurrent = $oCurrent[$x][$y] 389 | Case 3 ; 3D array 390 | Local $z = Int($aIndices[2]) 391 | If $z < 0 Or $z >= UBound($oCurrent, 3) Then Return SetError(5, $z, "") 392 | $oCurrent = $oCurrent[$x][$y][$z] 393 | Case Else 394 | Return SetError(7, @error, "") 395 | EndSwitch 396 | 397 | ; 1D array 398 | Else 399 | If UBound($oCurrent, 0) <> 1 Then Return SetError(6, UBound($oCurrent, 0), "") 400 | $d_Val = Int($aCurToken[1]) 401 | If $d_Val < 0 Or $d_Val >= UBound($oCurrent) Then Return SetError(5, $d_Val, "") 402 | $oCurrent = $oCurrent[$d_Val] 403 | EndIf 404 | EndIf 405 | Next 406 | Return $oCurrent 407 | EndFunc ;==>_JSON_Get 408 | 409 | 410 | ; #FUNCTION# ====================================================================================== 411 | ; Name ..........: _JSON_addChangeDelete 412 | ; Description ...: creates, modifies or deletes within nested AutoIt structures with a simple query string with syntax: 413 | ; MapKey#1.MapKey#2.[ArrayIndex#1].MapKey#3... (points keynames can be achieved by "\.") 414 | ; If the specified structure already exists, then the function overwrite the existing data. 415 | ; If the specified structure not exists, then the functions creates this structure. 416 | ; If $vVal = Default, then the function deletes this specific data point inside the structure. 417 | ; Syntax ........: _JSON_addChangeDelete(ByRef $oObject, Const $sPattern, Const $vVal = Default [, Const $iRecLevel = 0]) 418 | ; Parameters ....: $oObject - a nested AutoIt datastructure (Arrays, Maps, basic scalar types etc.) 419 | ; in which the structure is to be created or data is to be changed or deleted 420 | ; $sPattern - query pattern like described above 421 | ; $vVal - the value which should be written at the position in $sPattern 422 | ; - if $vVal = Default then the position in $sPattern is to be deleted 423 | ; $iRecLevel - don't touch! - only for internal purposes 424 | ; Return values .: Success - Return True 425 | ; Failure - Return False and set @error to: 426 | ; @error = 1 - pattern is not correct 427 | ; @error = 2 - wrong index for array element 428 | ; Author ........: AspirinJunkie 429 | ; ================================================================================================= 430 | Func _JSON_addChangeDelete(ByRef $oObject, Const $sPattern, Const $vVal = Default, Const $iRecLevel = 0) 431 | Local Static $aLevels[0] 432 | 433 | ; only on highest recursion level: process the selector string 434 | If $iRecLevel = 0 Then 435 | Local $aToken = StringRegExp($sPattern, '\[(\d+)\]|((?>\\.|[^\.\[\]\\]+)+)', 4) 436 | If @error Then Return SetError(1, @error, "") 437 | 438 | Local $aCurToken 439 | 440 | ReDim $aLevels[UBound($aToken) + 1][2] 441 | For $i = 0 To UBound($aToken) - 1 442 | $aCurToken = $aToken[$i] 443 | If UBound($aCurToken) = 3 Then ; KeyName 444 | $aLevels[$i][0] = "Map" 445 | $aLevels[$i][1] = StringRegExpReplace($aCurToken[2], '\\(.)', '$1') 446 | Else ; Array Index 447 | $aLevels[$i][0] = "Array" 448 | $aLevels[$i][1] = Int($aCurToken[1]) 449 | EndIf 450 | Next 451 | $aLevels[UBound($aLevels) - 1][0] = "end" 452 | EndIf 453 | 454 | ; If data structure not exists already - build it as stated in the selector string: 455 | Local $sCurrenttype = $aLevels[$iRecLevel][0] 456 | If $sCurrenttype <> VarGetType($oObject) Then 457 | Switch $sCurrenttype 458 | Case "Map" 459 | Local $mTmp[] 460 | $oObject = $mTmp 461 | Case "Array" 462 | Local $aTmp[$aLevels[$iRecLevel][1] + 1] 463 | $oObject = $aTmp 464 | Case "end" 465 | Return $vVal 466 | EndSwitch 467 | EndIf 468 | 469 | ; special case treatment for arrays 470 | If $sCurrenttype = "Array" Then 471 | If UBound($oObject, 0) <> 1 Then 472 | Local $aTmp[$aLevels[$iRecLevel][1] + 1] 473 | $oObject = $aTmp 474 | ElseIf UBound($oObject) < ($aLevels[$iRecLevel][1] + 1) Then 475 | ReDim $oObject[$aLevels[$iRecLevel][1] + 1] 476 | EndIf 477 | EndIf 478 | 479 | ; create or change the objects in the next hierarchical level and use these as value for the current entry 480 | Local $vTmp = $oObject[$aLevels[$iRecLevel][1]], _ 481 | $oNext = _JSON_addChangeDelete($vTmp, $sPattern, $vVal, $iRecLevel + 1) 482 | 483 | If $oNext = Default Then ; delete the current level 484 | Switch $sCurrenttype 485 | Case "Map" 486 | MapRemove($oObject, $aLevels[$iRecLevel][1]) 487 | Case "Array" 488 | Local $iInd = $aLevels[$iRecLevel][1], $nElems = UBound($oObject) 489 | 490 | If $iInd < 0 Or $iInd >= $nElems Then Return SetError(2, @error, "") 491 | 492 | For $i = $iInd To $nElems - 2 493 | $oObject[$i] = $oObject[$i + 1] 494 | Next 495 | ReDim $oObject[$nElems - 1] 496 | Case Else 497 | $oObject[$aLevels[$iRecLevel][1]] = "" 498 | For $j = UBound($oObject) - 1 To 0 Step -1 499 | If $oObject[$j] <> "" Then 500 | ReDim $oObject[$j + 1] 501 | ExitLoop 502 | EndIf 503 | Next 504 | EndSwitch 505 | Else 506 | $oObject[$aLevels[$iRecLevel][1]] = $oNext 507 | EndIf 508 | 509 | If $iRecLevel > 0 Then 510 | Return $oObject 511 | Else 512 | ReDim $aLevels[0] ; clean 513 | Return True 514 | EndIf 515 | EndFunc ;==>_JSON_addChangeDelete 516 | 517 | ; helper function for converting a json formatted string into an AutoIt-string 518 | Func __JSON_ParseString($sString) 519 | Local $cChar, $mChars[] 520 | 521 | Local $aRE = StringRegExp($sString, '\\\\(*SKIP)(*F)|\\(u[[:xdigit:]]{4}|[^nrt\\])', 3) 522 | If Not @error Then 523 | For $cChar In $aRE 524 | ; prevent double processing of already processed chars 525 | If MapExists($mChars, $cChar) Then ContinueLoop 526 | 527 | Switch StringLeft($cChar, 1) 528 | Case "b" 529 | $sString = StringRegExpReplace($sString, '\\\\(*SKIP)(*F)|\\b', Chr(8)) 530 | $mChars[$cChar] = "" 531 | Case "f" 532 | $sString = StringRegExpReplace($sString, '\\\\(*SKIP)(*F)|\\f', Chr(12)) 533 | $mChars[$cChar] = "" 534 | Case "u" 535 | $sString = StringRegExpReplace($sString, '\\\\(*SKIP)(*F)|\\' & $cChar, ChrW(Dec(StringTrimLeft($cChar, 1)))) 536 | $mChars[$cChar] = "" 537 | Case Else 538 | $sString = StringRegExpReplace($sString, '\\\\(*SKIP)(*F)|\\\Q' & $cChar & '\E', $cChar) 539 | $mChars[$cChar] = "" 540 | EndSwitch 541 | Next 542 | EndIf 543 | 544 | ; convert \n \r \t \\ 545 | Return StringFormat(StringReplace($sString, "%", "%%", 0, 1)) 546 | EndFunc ;==>__JSON_ParseString 547 | 548 | 549 | ; helper function for converting a AutoIt-string into a json formatted string 550 | Func __JSON_FormatString($sString) 551 | ; Quick check if any characters need escaping 552 | If Not StringRegExp($sString, '[\n\r\t"\\\b\f]') Then Return $sString 553 | 554 | ; Special chars found - choose replacement method by string length: 555 | ; for small strings direct RegExpReplace-method is faster; for long strings the manual method is better 556 | Return StringLen($sString) < 50 _ 557 | ? StringTrimRight(StringRegExpReplace($sString & '\\\b\f\n\r\t\"', '(?s)(?|\\(?=.*(\\\\))|[\b](?=.*(\\b))|\f(?=.*(\\f))|\r\n(?=.*(\\n))|\n(?=.*(\\n))|\r(?=.*(\\r))|\t(?=.*(\\t))|"(?=.*(\\")))', '\1'), 15) _ 558 | : StringReplace( _ 559 | StringReplace( _ 560 | StringReplace( _ 561 | StringReplace( _ 562 | StringReplace( _ 563 | StringReplace( _ 564 | StringReplace( _ 565 | StringReplace($sString, '\', '\\', 0, 1) _ 566 | , Chr(8), "\b", 0, 1) _ 567 | , Chr(12), "\f", 0, 1) _ 568 | , @CRLF, "\n", 0, 1) _ 569 | , @LF, "\n", 0, 1) _ 570 | , @CR, "\r", 0, 1) _ 571 | , @TAB, "\t", 0, 1) _ 572 | , '"', '\"', 0, 1) 573 | EndFunc 574 | 575 | 576 | ; #FUNCTION# ====================================================================================== 577 | ; Name ..........: __JSON_Base64Encode 578 | ; Description ...: convert a binary- or string-Input into BASE64 (or optional base64url) format 579 | ; mainly a wrapper for the CryptBinaryToString API-function 580 | ; Syntax ........: __JSON_Base64Encode(Const $sInput, [Const $b_base64url = False]) 581 | ; Parameters ....: $sInput - binary data or string which should be converted 582 | ; [$b_base64url] - If true the output is in base64url-format instead of base64 583 | ; Return values .: Success - Return base64 (or base64url) formatted string 584 | ; Failure - Return "" and set @error to: 585 | ; @error = 1 - failure at the first run to calculate the output size 586 | ; = 2 - failure at the second run to calculate the output 587 | ; Author ........: AspirinJunkie 588 | ; Example .......: Yes 589 | ; $sBase64String = __JSON_Base64Encode("This is my test") 590 | ; ================================================================================================= 591 | Func __JSON_Base64Encode(Const $sInput, Const $b_base64url = False) 592 | Local $b_Input = IsBinary($sInput) ? $sInput : Binary($sInput) 593 | 594 | Local $t_BinArray = DllStructCreate("BYTE[" & BinaryLen($sInput) & "]") 595 | DllStructSetData($t_BinArray, 1, $b_Input) 596 | 597 | Local $h_DLL_Crypt32 = DllOpen("Crypt32.dll") 598 | 599 | ; first run to calculate needed size of output buffer 600 | Local $aRet = DllCall($h_DLL_Crypt32, "BOOLEAN", "CryptBinaryToString", _ 601 | "STRUCT*", $t_BinArray, _ ; *pbBinary 602 | "DWORD", DllStructGetSize($t_BinArray), _ ; cbBinary 603 | "DWORD", 0x40000001, _ ; dwFlags 604 | "PTR", Null, _ ; pszString 605 | "DWORD*", 0) 606 | If @error Or Not IsArray($aRet) Or $aRet[0] = 0 Then Return SetError(1, @error, DllClose($h_DLL_Crypt32)) 607 | 608 | ; second run to calculate base64-string: 609 | Local $t_Output = DllStructCreate("CHAR Out[" & $aRet[5] & "]") 610 | Local $aRet2 = DllCall($h_DLL_Crypt32, "BOOLEAN", "CryptBinaryToString", _ 611 | "STRUCT*", $t_BinArray, _ ; *pbBinary 612 | "DWORD", DllStructGetSize($t_BinArray), _ ; cbBinary 613 | "DWORD", 0x40000001, _ ; dwFlags 614 | "STRUCT*", $t_Output, _ ; pszString 615 | "DWORD*", $aRet[5]) 616 | If @error Or Not IsArray($aRet2) Or $aRet2[0] = 0 Then Return SetError(2, @error, DllClose($h_DLL_Crypt32)) 617 | 618 | Local $sOutput = $t_Output.Out 619 | If StringInStr($sOutput, "=", 1, 1) Then $sOutput = StringLeft($sOutput, StringInStr($sOutput, "=", 1, 1) - 1) 620 | 621 | If $b_base64url Then $sOutput = StringReplace(StringReplace($sOutput, "/", "_", 0, 1), "+", "-", 0, 1) 622 | 623 | DllClose($h_DLL_Crypt32) 624 | Return $sOutput 625 | EndFunc ;==>__JSON_Base64Encode 626 | 627 | 628 | ; #FUNCTION# ====================================================================================== 629 | ; Name ..........: __JSON_Base64Decode 630 | ; Description ...: decode data which is coded as a base64-string into binary form 631 | ; mainly a wrapper for the CryptStringToBinary API-function 632 | ; Syntax ........: __JSON_Base64Decode(Const $sInput, [Const $b_base64url = False]) 633 | ; Parameters ....: $sInput - string in base64-format 634 | ; [$b_base64url] - If true the output is in base64url-format instead of base64 635 | ; Return values .: Success - Return base64 (or base64url) formatted string 636 | ; Failure - Return "" and set @error to: 637 | ; @error = 1 - failure at the first run to calculate the output size 638 | ; = 2 - failure at the second run to calculate the output 639 | ; Author ........: AspirinJunkie 640 | ; Example .......: Yes 641 | ; MsgBox(0, '', BinaryToString(__JSON_Base64Decode("VGVzdA"))) 642 | ; ================================================================================================= 643 | Func __JSON_Base64Decode(Const $sInput, Const $b_base64url = False) 644 | Local $h_DLL_Crypt32 = DllOpen("Crypt32.dll") 645 | 646 | ; first run to calculate needed size of output buffer 647 | Local $aRet = DllCall($h_DLL_Crypt32, "BOOLEAN", "CryptStringToBinary", _ 648 | "STR", $sInput, _ ; pszString 649 | "DWORD", 0, _ ; cchString 650 | "DWORD", 1, _ ; dwFlags 651 | "PTR", Null, _ ; pbBinary 652 | "DWORD*", 0, _ ; pcbBinary 653 | "PTR", Null, _ ; pdwSkip 654 | "PTR", Null) ; pdwFlags 655 | Local $t_Ret = DllStructCreate("BYTE Out[" & $aRet[5] & "]") 656 | If @error Or Not IsArray($aRet) Or $aRet[0] = 0 Then Return SetError(1, @error, DllClose($h_DLL_Crypt32)) 657 | 658 | 659 | ; second run to calculate the output data: 660 | Local $aRet2 = DllCall($h_DLL_Crypt32, "BOOLEAN", "CryptStringToBinary", _ 661 | "STR", $sInput, _ ; pszString 662 | "DWORD", 0, _ ; cchString 663 | "DWORD", 1, _ ; dwFlags 664 | "STRUCT*", $t_Ret, _ ; pbBinary 665 | "DWORD*", $aRet[5], _ ; pcbBinary 666 | "PTR", Null, _ ; pdwSkip 667 | "PTR", Null) ; pdwFlags 668 | If @error Or Not IsArray($aRet2) Or $aRet2[0] = 0 Then Return SetError(2, @error, DllClose($h_DLL_Crypt32)) 669 | DllClose($h_DLL_Crypt32) 670 | 671 | Local $sOutput = $t_Ret.Out 672 | If $b_base64url Then $sOutput = StringReplace(StringReplace($sOutput, "_", "/", 0, 1), "-", "+", 0, 1) 673 | 674 | Return $sOutput 675 | EndFunc ;==>__JSON_Base64Decode 676 | 677 | ; #FUNCTION# ====================================================================================== 678 | ; Name ..........: __JSON_A2DToAinA() 679 | ; Description ...: Convert a 2D array into a Arrays in Array 680 | ; Syntax ........: __JSON_A2DToAinA($A) 681 | ; Parameters ....: $A - the 2D-Array which should be converted 682 | ; Return values .: Success: a Arrays in Array build from the input array 683 | ; Failure: False 684 | ; @error = 1: $A is'nt an 2D array 685 | ; Author ........: AspirinJunkie 686 | ; Example .......: Yes 687 | ; #include 688 | ; 689 | ; Global $a2DArray[][] = [[1,2,3],[4,5,6],[7,8,9],[10,11,12]] 690 | ; 691 | ; For $aRow In __JSON_A2DToAinA($a2DArray) 692 | ; _ArrayDisplay($aRow, "single rows as 1D-Arrays") 693 | ; Next 694 | ; =================================================================================================# 695 | Func __JSON_A2DToAinA($A, $bTruncEmpty = True) 696 | If UBound($A, 0) <> 2 Then Return SetError(1, UBound($A, 0), False) 697 | Local $N = UBound($A), $u = UBound($A, 2) 698 | Local $aRet[$N] 699 | 700 | If $bTruncEmpty Then 701 | For $i = 0 To $N - 1 702 | Local $x = $u - 1 703 | While IsString($A[$i][$x]) And $A[$i][$x] = "" 704 | $x -= 1 705 | WEnd 706 | Local $t[$x + 1] 707 | For $j = 0 To $x 708 | $t[$j] = $A[$i][$j] 709 | Next 710 | $aRet[$i] = $t 711 | Next 712 | Else 713 | For $i = 0 To $N - 1 714 | Local $t[$u] 715 | For $j = 0 To $u - 1 716 | $t[$j] = $A[$i][$j] 717 | Next 718 | $aRet[$i] = $t 719 | Next 720 | EndIf 721 | Return $aRet 722 | EndFunc ;==>__JSON_A2DToAinA 723 | 724 | ; #FUNCTION# ====================================================================================== 725 | ; Name ..........: __JSON_AinAToA2d() 726 | ; Description ...: Convert a Arrays in Array into a 2D array 727 | ; here useful if you want to recover 2D-arrays from a json-string 728 | ; (there exists only a array-in-array and no 2D-Arrays) 729 | ; Syntax ........: __JSON_AinAToA2d($A) 730 | ; Parameters ....: $A - the arrays in array which should be converted 731 | ; Return values .: Success: a 2D Array build from the input array 732 | ; Failure: False 733 | ; @error = 1: $A is'nt an 1D array 734 | ; = 2: $A is empty 735 | ; = 3: first element isn't a array 736 | ; Author ........: AspirinJunkie 737 | ; ================================================================================================= 738 | Func __JSON_AinAToA2d($A) 739 | If UBound($A, 0) <> 1 Then Return SetError(1, UBound($A, 0), False) 740 | Local $N = UBound($A) 741 | If $N < 1 Then Return SetError(2, $N, False) 742 | Local $u = UBound($A[0]) 743 | If $u < 1 Then Return SetError(3, $u, False) 744 | 745 | Local $aRet[$N][$u] 746 | 747 | For $i = 0 To $N - 1 748 | Local $t = $A[$i] 749 | If UBound($t) > $u Then ReDim $aRet[$N][UBound($t)] 750 | For $j = 0 To UBound($t) - 1 751 | $aRet[$i][$j] = $t[$j] 752 | Next 753 | Next 754 | Return $aRet 755 | EndFunc ;==>__JSON_AinAToA2d 756 | -------------------------------------------------------------------------------- /JSON_Dictionary.au3: -------------------------------------------------------------------------------- 1 | #include-once 2 | #include 3 | #include 4 | 5 | ; if AutoIt-Version without maps is used 6 | #ignorefunc MapExists, MapKeys 7 | 8 | 9 | ; #FUNCTION# ====================================================================================== 10 | ; Name ..........: _JSON_Get 11 | ; Description ...: query nested AutoIt-datastructure with a simple query string with syntax: 12 | ; DictionaryKey1.DictionaryKey2.[ArrayIndex1].DictionaryKey3... 13 | ; Syntax ........: _JSON_Get(ByRef $o_Object, Const $s_Pattern) 14 | ; Parameters ....: $o_Object - a nested AutoIt datastructure (Arrays, Dictionaries, basic scalar types) 15 | ; $s_Pattern - query pattern like described above 16 | ; Return values .: Success - Return the queried object out of the nested datastructure 17 | ; Failure - Return "" and set @error to: 18 | ; @error = 1 - pattern is not correct 19 | ; = 2 - keyname query to none dictionary object 20 | ; = 3 - keyname queried not exists in dictionary 21 | ; = 4 - index query on none array object 22 | ; = 5 - index out of array range 23 | ; Author ........: AspirinJunkie 24 | ; ================================================================================================= 25 | Func _JSON_Get(ByRef $o_Object, Const $s_Pattern) 26 | Local $o_Current = $o_Object, $d_Val 27 | Local $a_Tokens = StringRegExp($s_Pattern, '\[(\d+)\]|([^\.\[\]]+)', 4) 28 | If @error Then Return SetError(1, @error, "") 29 | 30 | For $a_CurToken In $a_Tokens 31 | 32 | If UBound($a_CurToken) = 3 Then ; KeyName 33 | Switch VarGetType($o_Current) 34 | Case "Object" 35 | If Not IsObj($o_Current) Or ObjName($o_Current) <> "Dictionary" Then Return SetError(2, 0, "") 36 | If Not $o_Current.Exists($a_CurToken[2]) Then Return SetError(3, 0, "") 37 | 38 | $o_Current = $o_Current($a_CurToken[2]) 39 | Case "Map" 40 | If Not MapExists($o_Current, $a_CurToken[2]) Then Return SetError(3, 0, "") 41 | 42 | $o_Current = $o_Current[$a_CurToken[2]] 43 | EndSwitch 44 | ElseIf UBound($a_CurToken) = 2 Then ; ArrayIndex 45 | If (Not IsArray($o_Current)) Or UBound($o_Current, 0) <> 1 Then Return SetError(4, UBound($o_Current, 0), "") 46 | $d_Val = Int($a_CurToken[1]) 47 | If $d_Val < 0 Or $d_Val >= UBound($o_Current) Then Return SetError(5, $d_Val, "") 48 | $o_Current = $o_Current[$d_Val] 49 | EndIf 50 | Next 51 | Return $o_Current 52 | EndFunc ;==>_JSON_Get 53 | 54 | 55 | ; #FUNCTION# ====================================================================================== 56 | ; Name ..........: _JSON_Generate 57 | ; Description ...: convert a JSON-formatted string into a nested structure of AutoIt-datatypes 58 | ; Syntax ........: _JSON_Generate($o_Object, $s_ObjIndent = @TAB, $s_ObjDelEl = @CRLF, $s_ObjDelKey = " ", $s_ObjDelVal = "", $s_ArrIndent = @TAB, $s_ArrDelEl = @CRLF, $i_Level = 0) 59 | ; Parameters ....: $s_String - a string formatted as JSON 60 | ; [$s_ObjIndent] - indent for object elements (only reasonable if $s_ObjDelEl contains a line skip 61 | ; [$s_ObjDelEl] - delimiter between object elements 62 | ; [$s_ObjDelKey] - delimiter between keyname and ":" in object 63 | ; [$s_ObjDelVal] - delimiter between ":" and value in object 64 | ; [$s_ArrIndent] - indent for array elements (only reasonable if $s_ArrDelEl contains a line skip) 65 | ; [$s_ArrDelEl] - delimiter between array elements 66 | ; [$i_Level] - search position where to start (normally don't touch!) 67 | ; Return values .: Success - Return a JSON formatted string 68 | ; Failure - Return "" 69 | ; Author ........: AspirinJunkie 70 | ; ================================================================================================= 71 | Func _JSON_Generate($o_Object, $s_ObjIndent = @TAB, $s_ObjDelEl = @CRLF, $s_ObjDelKey = "", $s_ObjDelVal = " ", $s_ArrIndent = @TAB, $s_ArrDelEl = @CRLF, $i_Level = 0) 72 | Local Static $s_JSON_String 73 | If $i_Level = 0 Then $s_JSON_String = "" 74 | 75 | Switch VarGetType($o_Object) 76 | Case "String" 77 | __JSON_FormatString($o_Object) 78 | $s_JSON_String &= '"' & $o_Object & '"' 79 | Case "Int32", "Int64", "Float", "Double" 80 | $s_JSON_String &= String($o_Object) 81 | ;~ $s_JSON_String &= StringRegExpReplace(StringFormat("%g", $o_Object), '(-?(?>0|[1-9]\d*)(?>\.\d+)?)(?:([eE][-+]?)0*(\d+))?', "$1$2$3", 1) 82 | Case "Bool" 83 | $s_JSON_String &= StringLower($o_Object) 84 | Case "Keyword" 85 | If IsKeyword($o_Object) = 2 Then $s_JSON_String &= "null" 86 | Case "Binary" 87 | $s_JSON_String &= '"' & _Base64Encode($o_Object) & '"' 88 | Case "Array" 89 | If UBound($o_Object, 0) = 2 Then $o_Object = __Array2dToAinA($o_Object) 90 | If UBound($o_Object) = 0 Then 91 | $s_JSON_String &= "[]" 92 | Else 93 | $s_JSON_String &= "[" & $s_ArrDelEl 94 | For $o_Value In $o_Object 95 | $s_JSON_String &= _StringRepeat($s_ArrIndent, $i_Level + 1) 96 | _JSON_Generate($o_Value, $s_ObjIndent, $s_ObjDelEl, $s_ObjDelKey, $s_ObjDelVal, $s_ArrIndent, $s_ArrDelEl, $i_Level + 1) 97 | 98 | $s_JSON_String &= "," & $s_ArrDelEl 99 | Next 100 | $s_JSON_String = StringTrimRight($s_JSON_String, StringLen("," & $s_ArrDelEl)) & $s_ArrDelEl & _StringRepeat($s_ArrIndent, $i_Level) & "]" 101 | EndIf 102 | Case "Object" 103 | If ObjName($o_Object) = "Dictionary" Then 104 | Local $s_KeyTemp, $o_Value 105 | If $o_Object.Count() = 0 Then 106 | $s_JSON_String &= "{}" 107 | Else 108 | $s_JSON_String &= "{" & $s_ObjDelEl 109 | For $s_Key In $o_Object.Keys 110 | $s_KeyTemp = $s_Key 111 | $o_Value = $o_Object($s_Key) 112 | __JSON_FormatString($s_KeyTemp) 113 | 114 | $s_JSON_String &= _StringRepeat($s_ObjIndent, $i_Level + 1) & '"' & $s_KeyTemp & '"' & $s_ObjDelKey & ':' & $s_ObjDelVal 115 | 116 | _JSON_Generate($o_Value, $s_ObjIndent, $s_ObjDelEl, $s_ObjDelKey, $s_ObjDelVal, $s_ArrIndent, $s_ArrDelEl, $i_Level + 1) 117 | 118 | $s_JSON_String &= "," & $s_ObjDelEl 119 | Next 120 | $s_JSON_String = StringTrimRight($s_JSON_String, StringLen("," & $s_ObjDelEl)) & $s_ObjDelEl & _StringRepeat($s_ObjIndent, $i_Level) & "}" 121 | EndIf 122 | EndIf 123 | Case "Map" 124 | Local $s_KeyTemp, $o_Value 125 | If UBound($o_Object) = 0 Then 126 | $s_JSON_String &= "{}" 127 | Else 128 | $s_JSON_String &= "{" & $s_ObjDelEl 129 | For $s_Key In MapKeys($o_Object) 130 | $s_KeyTemp = $s_Key 131 | $o_Value = $o_Object[$s_Key] 132 | __JSON_FormatString($s_KeyTemp) 133 | 134 | $s_JSON_String &= _StringRepeat($s_ObjIndent, $i_Level + 1) & '"' & $s_KeyTemp & '"' & $s_ObjDelKey & ':' & $s_ObjDelVal 135 | 136 | _JSON_Generate($o_Value, $s_ObjIndent, $s_ObjDelEl, $s_ObjDelKey, $s_ObjDelVal, $s_ArrIndent, $s_ArrDelEl, $i_Level + 1) 137 | 138 | $s_JSON_String &= "," & $s_ObjDelEl 139 | Next 140 | $s_JSON_String = StringTrimRight($s_JSON_String, StringLen("," & $s_ObjDelEl)) & $s_ObjDelEl & _StringRepeat($s_ObjIndent, $i_Level) & "}" 141 | EndIf 142 | EndSwitch 143 | 144 | If $i_Level = 0 Then 145 | Local $s_Temp = $s_JSON_String 146 | $s_JSON_String = "" 147 | Return $s_Temp 148 | EndIf 149 | EndFunc ;==>_JSON_Generate 150 | 151 | 152 | ; #FUNCTION# ====================================================================================== 153 | ; Name ..........: _JSON_Parse 154 | ; Description ...: convert a JSON-formatted string into a nested structure of AutoIt-datatypes 155 | ; Syntax ........: _JSON_Parse(ByRef $s_String, $i_Os = 1) 156 | ; Parameters ....: $s_String - a string formatted as JSON 157 | ; [$bUseMaps] - If true: maps used instead of scripting.dictionary for objects (available only for special AutoIt-versions) 158 | ; [$i_Os] - search position where to start (normally don't touch!) 159 | ; Return values .: Success - Return a nested structure of AutoIt-datatypes 160 | ; @extended = next string offset 161 | ; Failure - Return "" and set @error to: 162 | ; @error = 1 - part is not json-syntax 163 | ; = 2 - key name in object part is not json-syntax 164 | ; = 3 - value in object is not correct json 165 | ; = 4 - delimiter or object end expected but not gained 166 | ; Author ........: AspirinJunkie 167 | ; ================================================================================================= 168 | Func _JSON_Parse(ByRef $s_String, Const $bUseMaps = False, $i_Os = 1) 169 | Local $i_OsC = $i_Os, $o_Current, $o_Value 170 | ; Inside a character class, \R is treated as an unrecognized escape sequence, and so matches the letter "R" by default, but causes an error if 171 | Local Static $s_RE_s = '[\x20\r\n\t]', _ ; = [\x20\x09\x0A\x0D] 172 | $s_RE_G_String = '\G[\x20\r\n\t]*"((?>[^\\"]+|\\.)*+)"', _ ; only for real valid JSON: "((?>[^\\"]+|\\[\\"bfnrtu\/])*)" second (a little slower) alternative: "((?>[^\\"]+|\\\\|\\.)*)" 173 | $s_RE_G_Number = '\G[\x20\r\n\t]*(-?(?>0|[1-9]\d*)(?>\.\d+)?(?>[eE][-+]?\d+)?)', _ 174 | $s_RE_G_KeyWord = '\G[\x20\r\n\t]*\b(null|true|false)\b', _ 175 | $s_RE_G_Object_Begin = '\G[\x20\r\n\t]*\{', _ 176 | $s_RE_G_Object_Key = '\G[\x20\r\n\t]*"((?>[^\\"]+|\\.)*+)"[\x20\r\n\t]*:', _ 177 | $s_RE_G_Object_Further = '\G[\x20\r\n\t]*,', _ 178 | $s_RE_G_Object_End = '\G[\x20\r\n\t]*\}', _ 179 | $s_RE_G_Array_Begin = '\G[\x20\r\n\t]*\[', _ 180 | $s_RE_G_Array_End = '\G[\x20\r\n\t]*\]' 181 | 182 | $o_Current = StringRegExp($s_String, $s_RE_G_String, 1, $i_Os) ; String 183 | If Not @error Then Return SetExtended(@extended, __JSON_ParseString($o_Current[0])) 184 | 185 | StringRegExp($s_String, $s_RE_G_Object_Begin, 1, $i_Os) ; Object 186 | If Not @error Then 187 | $i_OsC = @extended 188 | Local $s_Key, $o_Value, $a_T 189 | 190 | If $bUseMaps Then 191 | Local $o_Current[] 192 | Else 193 | $o_Current = ObjCreate("Scripting.Dictionary") 194 | EndIf 195 | 196 | StringRegExp($s_String, $s_RE_G_Object_End, 1, $i_OsC) ; check for empty object 197 | If Not @error Then ; empty object 198 | $i_OsC = @extended 199 | Else 200 | Do 201 | $a_T = StringRegExp($s_String, $s_RE_G_Object_Key, 1, $i_OsC) ; key of element 202 | If @error Then Return SetError(2, $i_OsC, "") 203 | $i_OsC = @extended 204 | 205 | $s_Key = __JSON_ParseString($a_T[0]) 206 | 207 | $o_Value = _JSON_Parse($s_String, $bUseMaps, $i_OsC) 208 | If @error Then Return SetError(3, $i_OsC, "") 209 | $i_OsC = @extended 210 | 211 | If $bUseMaps Then 212 | $o_Current[$s_Key] = $o_Value ; add key:value to map 213 | Else 214 | $o_Current($s_Key) = $o_Value ; add key:value to dictionary 215 | EndIf 216 | 217 | StringRegExp($s_String, $s_RE_G_Object_Further, 1, $i_OsC) ; more elements 218 | If Not @error Then 219 | $i_OsC = @extended 220 | ContinueLoop 221 | Else 222 | StringRegExp($s_String, $s_RE_G_Object_End, 1, $i_OsC) ; end of array 223 | If Not @error Then 224 | $i_OsC = @extended 225 | ExitLoop 226 | Else 227 | Return SetError(4, $i_OsC, "") 228 | EndIf 229 | EndIf 230 | Until False 231 | EndIf 232 | 233 | Return SetExtended($i_OsC, $o_Current) 234 | EndIf 235 | 236 | 237 | StringRegExp($s_String, $s_RE_G_Array_Begin, 1, $i_Os) ; Array 238 | If Not @error Then 239 | $i_OsC = @extended 240 | 241 | Local $o_Current[1], $d_N = 1, $i_C = 0 242 | 243 | StringRegExp($s_String, $s_RE_G_Array_End, 1, $i_OsC) ; check for empty array 244 | If Not @error Then ; empty array 245 | ReDim $o_Current[0] 246 | $i_OsC = @extended 247 | Return SetExtended($i_OsC, $o_Current) 248 | EndIf 249 | 250 | Do 251 | $o_Value = _JSON_Parse($s_String, $bUseMaps, $i_OsC) 252 | If @error Then Return SetError(3, $i_OsC, "") 253 | $i_OsC = @extended 254 | 255 | If $i_C = $d_N - 1 Then 256 | $d_N *= 2 257 | ReDim $o_Current[$d_N] 258 | EndIf 259 | $o_Current[$i_C] = $o_Value 260 | $i_C += 1 261 | 262 | StringRegExp($s_String, $s_RE_G_Object_Further, 1, $i_OsC) ; more elements 263 | If Not @error Then 264 | $i_OsC = @extended 265 | ContinueLoop 266 | Else 267 | StringRegExp($s_String, $s_RE_G_Array_End, 1, $i_OsC) ; end of array 268 | If Not @error Then 269 | $i_OsC = @extended 270 | ExitLoop 271 | Else 272 | Return SetError(5, $i_OsC, "") 273 | EndIf 274 | EndIf 275 | 276 | Until False 277 | If UBound($o_Current) <> $i_C Then ReDim $o_Current[$i_C] 278 | Return SetExtended($i_OsC, $o_Current) 279 | EndIf 280 | 281 | $o_Current = StringRegExp($s_String, $s_RE_G_Number, 1, $i_Os) ; Number 282 | If Not @error Then Return SetExtended(@extended, Number(StringLower($o_Current[0]))) ; Note: StringReplace(...,"E", "e") = Workaround for Number()-Bug: https://www.autoitscript.com/trac/autoit/ticket/3800#ticket 283 | 284 | $o_Current = StringRegExp($s_String, $s_RE_G_KeyWord, 1, $i_Os) ; KeyWord 285 | If Not @error Then Return SetExtended(@extended, Execute($o_Current[0])) ; $o_Current[0] = "null" ? Null : $o_Current[0] = "true" ? True : False) 286 | 287 | Return SetError(1, $i_OsC, "") 288 | EndFunc ;==>_JSON_Parse 289 | 290 | 291 | ; helper function for converting a json formatted string into an AutoIt-string 292 | Func __JSON_ParseString(ByRef $s_String) 293 | Local $aB[5] 294 | 295 | Local $a_RE = StringRegExp($s_String, '\\\\(*SKIP)(?!)|(\\["bf/]|\\u[[:xdigit:]]{4})', 3) 296 | If Not @error Then 297 | For $s_Esc In $a_RE 298 | Switch StringMid($s_Esc, 2, 1) 299 | Case "b" 300 | If $aB[0] Then ContinueLoop 301 | $s_String = StringRegExpReplace($s_String, '\\\\(*SKIP)(*FAIL)|\\b', Chr(8)) 302 | $aB[0] = True 303 | Case "f" 304 | If $aB[1] Then ContinueLoop 305 | $s_String = StringRegExpReplace($s_String, '\\\\(*SKIP)(*FAIL)|\\f', Chr(12)) 306 | $aB[1] = True 307 | Case "/" 308 | If $aB[2] Then ContinueLoop 309 | $s_String = StringRegExpReplace($s_String, '\\\\(*SKIP)(*FAIL)|\\/', "/") 310 | $aB[2] = True 311 | Case '"' 312 | If $aB[3] Then ContinueLoop 313 | $s_String = StringRegExpReplace($s_String, '\\\\(*SKIP)(*FAIL)|\\"', '"') 314 | $aB[3] = True 315 | Case "u" 316 | If $aB[4] Then ContinueLoop 317 | Local $a_RE = StringRegExp($s_String, '\\\\(*SKIP)(?!)|\\u\K[[:xdigit:]]{4}', 3) 318 | If Not @error Then 319 | If UBound($a_RE) > 10 Then _ArrayUnique($a_RE) 320 | For $s_Code In $a_RE 321 | $s_String = StringReplace($s_String, "\u" & $s_Code, ChrW(Dec($s_Code)), 0, 1) 322 | Next 323 | $aB[4] = True 324 | EndIf 325 | EndSwitch 326 | Next 327 | EndIf 328 | 329 | ; converts \n \r \t \\ implicit: 330 | Return StringFormat(StringReplace($s_String, "%", "%%", 0, 1)) 331 | EndFunc ;==>__JSON_ParseString 332 | 333 | ; helper function for converting a AutoIt-sting into a json formatted string 334 | Func __JSON_FormatString(ByRef $s_String) 335 | $s_String = _ 336 | StringReplace( _ 337 | StringReplace( _ 338 | StringReplace( _ 339 | StringReplace( _ 340 | StringReplace( _ 341 | StringReplace( _ 342 | StringReplace( _ 343 | StringReplace($s_String, '\', '\\', 0, 1) _ 344 | , Chr(8), "\b", 0, 1) _ 345 | , Chr(12), "\f", 0, 1) _ 346 | , @CRLF, "\n", 0, 1) _ 347 | , @LF, "\n", 0, 1) _ 348 | , @CR, "\r", 0, 1) _ 349 | , @TAB, "\t", 0, 1) _ 350 | , '"', '\"', 0, 1) 351 | EndFunc ;==>__JSON_FormatString 352 | 353 | 354 | ; #FUNCTION# ====================================================================================== 355 | ; Name ..........: _Base64Encode 356 | ; Description ...: convert a binary- or string-Input into BASE64 (or optional base64url) format 357 | ; mainly a wrapper for the CryptBinaryToString API-function 358 | ; Syntax ........: _Base64Encode(Const ByRef $s_Input, [Const $b_base64url = False]) 359 | ; Parameters ....: $s_Input - binary data or string which should be converted 360 | ; [$b_base64url] - If true the output is in base64url-format instead of base64 361 | ; Return values .: Success - Return base64 (or base64url) formatted string 362 | ; Failure - Return "" and set @error to: 363 | ; @error = 1 - failure at the first run to calculate the output size 364 | ; = 2 - failure at the second run to calculate the output 365 | ; Author ........: AspirinJunkie 366 | ; Example .......: Yes 367 | ; $s_Base64String = _Base64Encode("This is my test") 368 | ; ================================================================================================= 369 | Func _Base64Encode(Const ByRef $s_Input, Const $b_base64url = False) 370 | Local $b_Input = IsBinary($s_Input) ? $s_Input : Binary($s_Input) 371 | 372 | Local $t_BinArray = DllStructCreate("BYTE[" & BinaryLen($s_Input) & "]") 373 | DllStructSetData($t_BinArray, 1, $b_Input) 374 | 375 | Local $h_DLL_Crypt32 = DllOpen("Crypt32.dll") 376 | 377 | ; first run to calculate needed size of output buffer 378 | Local $a_Ret = DllCall($h_DLL_Crypt32, "BOOLEAN", "CryptBinaryToString", _ 379 | "STRUCT*", $t_BinArray, _ ; *pbBinary 380 | "DWORD", DllStructGetSize($t_BinArray), _ ; cbBinary 381 | "DWORD", 1, _ ; dwFlags 382 | "PTR", Null, _ ; pszString 383 | "DWORD*", 0) 384 | If @error Or Not IsArray($a_Ret) Or $a_Ret[0] = 0 Then Return SetError(1, @error, DllClose($h_DLL_Crypt32)) 385 | 386 | ; second run to calculate base64-string: 387 | Local $t_Output = DllStructCreate("CHAR Out[" & $a_Ret[5] & "]") 388 | Local $a_Ret2 = DllCall($h_DLL_Crypt32, "BOOLEAN", "CryptBinaryToString", _ 389 | "STRUCT*", $t_BinArray, _ ; *pbBinary 390 | "DWORD", DllStructGetSize($t_BinArray), _ ; cbBinary 391 | "DWORD", 1, _ ; dwFlags 392 | "STRUCT*", $t_Output, _ ; pszString 393 | "DWORD*", $a_Ret[5]) 394 | If @error Or Not IsArray($a_Ret2) Or $a_Ret2[0] = 0 Then Return SetError(2, @error, DllClose($h_DLL_Crypt32)) 395 | 396 | Local $s_Output = $t_Output.Out 397 | If StringInStr($s_Output, "=", 1, 1) Then $s_Output = StringLeft($s_Output, StringInStr($s_Output, "=", 1, 1) - 1) 398 | 399 | If $b_base64url Then $s_Output = StringReplace(StringReplace($s_Output, "/", "_", 0, 1), "+", "-", 0, 1) 400 | 401 | DllClose($h_DLL_Crypt32) 402 | Return $s_Output 403 | EndFunc ;==>_Base64Encode 404 | 405 | 406 | ; #FUNCTION# ====================================================================================== 407 | ; Name ..........: _Base64Decode 408 | ; Description ...: decode data which is coded as a base64-string into binary form 409 | ; mainly a wrapper for the CryptStringToBinary API-function 410 | ; Syntax ........: _Base64Decode(Const ByRef $s_Input, [Const $b_base64url = False]) 411 | ; Parameters ....: $s_Input - string in base64-format 412 | ; [$b_base64url] - If true the output is in base64url-format instead of base64 413 | ; Return values .: Success - Return base64 (or base64url) formatted string 414 | ; Failure - Return "" and set @error to: 415 | ; @error = 1 - failure at the first run to calculate the output size 416 | ; = 2 - failure at the second run to calculate the output 417 | ; Author ........: AspirinJunkie 418 | ; Example .......: Yes 419 | ; MsgBox(0, '', BinaryToString(_Base64Decode("VGVzdA"))) 420 | ; ================================================================================================= 421 | Func _Base64Decode(Const ByRef $s_Input, Const $b_base64url = False) 422 | Local $h_DLL_Crypt32 = DllOpen("Crypt32.dll") 423 | 424 | ; hier noch einen Reg-Ex zum testen ob String base64-codiert ist 425 | 426 | ; first run to calculate needed size of output buffer 427 | Local $a_Ret = DllCall($h_DLL_Crypt32, "BOOLEAN", "CryptStringToBinary", _ 428 | "STR", $s_Input, _ ; pszString 429 | "DWORD", 0, _ ; cchString 430 | "DWORD", 1, _ ; dwFlags 431 | "PTR", Null, _ ; pbBinary 432 | "DWORD*", 0, _ ; pcbBinary 433 | "PTR", Null, _ ; pdwSkip 434 | "PTR", Null) ; pdwFlags 435 | Local $t_Ret = DllStructCreate("BYTE Out[" & $a_Ret[5] & "]") 436 | If @error Or Not IsArray($a_Ret) Or $a_Ret[0] = 0 Then Return SetError(1, @error, DllClose($h_DLL_Crypt32)) 437 | 438 | 439 | ; second run to calculate the output data: 440 | Local $a_Ret2 = DllCall($h_DLL_Crypt32, "BOOLEAN", "CryptStringToBinary", _ 441 | "STR", $s_Input, _ ; pszString 442 | "DWORD", 0, _ ; cchString 443 | "DWORD", 1, _ ; dwFlags 444 | "STRUCT*", $t_Ret, _ ; pbBinary 445 | "DWORD*", $a_Ret[5], _ ; pcbBinary 446 | "PTR", Null, _ ; pdwSkip 447 | "PTR", Null) ; pdwFlags 448 | If @error Or Not IsArray($a_Ret2) Or $a_Ret2[0] = 0 Then Return SetError(2, @error, DllClose($h_DLL_Crypt32)) 449 | DllClose($h_DLL_Crypt32) 450 | 451 | Local $s_Output = $t_Ret.Out 452 | If $b_base64url Then $s_Output = StringReplace(StringReplace($s_Output, "_", "/", 0, 1), "-", "+", 0, 1) 453 | 454 | Return $s_Output 455 | EndFunc ;==>_Base64Decode 456 | 457 | ; #FUNCTION# ====================================================================================== 458 | ; Name ..........: __Array2dToAinA() 459 | ; Description ...: Convert a 2D array into a Arrays in Array 460 | ; Syntax ........: _Array2dToAinA(ByRef $A) 461 | ; Parameters ....: $A - the 2D-Array which should be converted 462 | ; Return values .: Success: a Arrays in Array build from the input array 463 | ; Failure: False 464 | ; @error = 1: $A is'nt an 2D array 465 | ; Author ........: AspirinJunkie 466 | ; ================================================================================================= 467 | Func __Array2dToAinA(ByRef $A, $bTruncEmpty = True) 468 | If UBound($A, 0) <> 2 Then Return SetError(1, UBound($A, 0), False) 469 | Local $N = UBound($A), $u = UBound($A, 2) 470 | Local $a_Ret[$N] 471 | 472 | IF $bTruncEmpty Then 473 | For $i = 0 To $N - 1 474 | Local $x = $u -1 475 | While IsString($A[$i][$x]) And $A[$i][$x] = "" 476 | $x -= 1 477 | WEnd 478 | Local $t[$x+1] 479 | For $j = 0 To $x 480 | $t[$j] = $A[$i][$j] 481 | Next 482 | $a_Ret[$i] = $t 483 | Next 484 | Else 485 | For $i = 0 To $N - 1 486 | Local $t[$u] 487 | For $j = 0 To $u - 1 488 | $t[$j] = $A[$i][$j] 489 | Next 490 | $a_Ret[$i] = $t 491 | Next 492 | EndIf 493 | Return $a_Ret 494 | EndFunc ;==>__Array2dToAinA 495 | 496 | ; #FUNCTION# ====================================================================================== 497 | ; Name ..........: __ArrayAinATo2d() 498 | ; Description ...: Convert a Arrays in Array into a 2D array 499 | ; Syntax ........: __ArrayAinATo2d(ByRef $A) 500 | ; Parameters ....: $A - the arrays in array which should be converted 501 | ; Return values .: Success: a 2D Array build from the input array 502 | ; Failure: False 503 | ; @error = 1: $A is'nt an 1D array 504 | ; = 2: $A is empty 505 | ; = 3: first element isn't a array 506 | ; Author ........: AspirinJunkie 507 | ; ================================================================================================= 508 | Func __ArrayAinATo2d(ByRef $A) 509 | If UBound($A, 0) <> 1 Then Return SetError(1, UBound($A, 0), False) 510 | Local $N = UBound($A) 511 | If $N < 1 Then Return SetError(2, $N, False) 512 | Local $u = UBound($A[0]) 513 | If $u < 1 Then Return SetError(3, $u, False) 514 | 515 | Local $a_Ret[$N][$u] 516 | 517 | For $i = 0 To $N - 1 518 | Local $t = $A[$i] 519 | If UBound($t) > $u Then ReDim $a_Ret[$N][UBound($t)] 520 | For $j = 0 To UBound($t) - 1 521 | $a_Ret[$i][$j] = $t[$j] 522 | Next 523 | Next 524 | Return $a_Ret 525 | EndFunc ;==>__ArrayAinATo2d 526 | 527 | 528 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | JSON is a pure data exchange format. Basically you only have to deal with JSON in 2 places in a program: Once when reading JSON data and once when outputting data. 3 | In between it should not really matter that the data used to be JSON or should be converted to it. 4 | You should not need any special intermediate structures but only the elements that the respective programming language provides anyway. 5 | 6 | This is exactly the approach of this UDF: 7 | There is the function _JSON_Parse(), which converts an arbitrary JSON string into (nested) pure AutoIt data types (Arrays, Maps, Strings, Numbers, Null, True, False). 8 | And on the other side we have the function _JSON_Generate(), which generates a JSON string from arbitrary (nested) AutoIt data structures. 9 | 10 | ## Import and export JSON 11 | So how to use - let`s give an example: 12 | 13 | ```AutoIt 14 | #include "JSON.au3" 15 | 16 | Global $s_String = '[{"id":"4434156","url":"https://legacy.sky.com/v2/schedules/4434156","title":"468_CORE_1_R.4 Schedule","time_zone":"London","start_at":"2017/08/10 19:00:00 +0100","end_at":null,"notify_user":false,"delete_at_end":false,"executions":[],"recurring_days":[],"actions":[{"type":"run","offset":0}],"next_action_name":"run","next_action_time":"2017/08/10 14:00:00 -0400","user":{"id":"9604","url":"https://legacy.sky.com/v2/users/9604","login_name":"robin@ltree.com","first_name":"Robin","last_name":"John","email":"robin@ltree.com","role":"admin","deleted":false},"region":"EMEA","can_edit":true,"vm_ids":null,"configuration_id":"19019196","configuration_url":"https://legacy.sky.com/v2/configurations/19019196","configuration_name":"468_CORE_1_R.4"},{"id":"4444568","url":"https://legacy.sky.com/v2/schedules/4444568","title":"468_CORE_1_R.4 Schedule","time_zone":"London","start_at":"2017/08/11 12:00:00 +0100","end_at":null,"notify_user":false,"delete_at_end":false,"executions":[],"recurring_days":[],"actions":[{"type":"suspend","offset":0}],"next_action_name":"suspend","next_action_time":"2017/08/11 07:00:00 -0400","user":{"id":"9604","url":"https://legacy.sky.com/v2/users/9604","login_name":"robin@ltree.com","first_name":"Robin","last_name":"John","email":"robin@ltree.com","role":"admin","deleted":false},"region":"EMEA","can_edit":true,"vm_ids":null,"configuration_id":"19019196","configuration_url":"https://legacy.sky.com/v2/configurations/19019196","configuration_name":"468_CORE_1_R.4"}]' 17 | 18 | ; ================= parse the JSON-String into a nested AutoIt data structure ============== 19 | $o_Object = _JSON_Parse($s_String) 20 | 21 | ; ================= query values from the structure directly with AutoIt syntax ============ 22 | $s_Type = $o_Object[1].actions[0].type 23 | ConsoleWrite("type: " & $s_Type & @CRLF) 24 | 25 | ; ; ================= query values via _JSON_Get() (safer and clearer) ======================= 26 | $s_Type = _JSON_Get($o_Object, "[1].actions[0].type") 27 | ConsoleWrite("type: " & $s_Type & @CRLF & @CRLF) 28 | 29 | ; ; ================= convert AutoIt data structures into a JSON string ====================== 30 | ConsoleWrite(_JSON_Generate($o_Object) & @CRLF & @CRLF) 31 | ; ; compact form: 32 | ConsoleWrite(_JSON_Generate($o_Object, "", "", "", "", "", "") & @CRLF & @CRLF) 33 | ``` 34 | 35 | ## Handling nested data structures 36 | JSON is often very nested. The resulting AutoIt data is therefore naturally also nested, which makes it somewhat cumbersome to process with pure AutoIt on-board methods. 37 | 38 | For this reason, the UDF comes with a few helper functions that make life with this data easier. 39 | One of them is _JSON_Get(), which allows you to access deeply nested data with a simple query syntax. 40 | On the other hand there is the function _JSON_addChangeDelete() with which you can (the name already says it) change, add and delete data. 41 | You can even easily create deeply nested structures with a single call. 42 | 43 | Again, here is a small example of how to use it: 44 | ```AutoIt 45 | #include "JSON.au3" 46 | 47 | Global $mMap ; target variable 48 | 49 | ; Create a structure to manage the employees of different companies in their respective company sites: 50 | _JSON_addChangeDelete($mMap, "our company.company sites[1].employee[0]", "John Johnson") 51 | _JSON_addChangeDelete($mMap, "our company.company sites[1].employee[1]", "Margret Margretson") 52 | _JSON_addChangeDelete($mMap, "our company.company sites[3].employee[0]", "Betty Bettinson") 53 | 54 | ; Change a value - e.g. replace the employee "John Johnson" 55 | _JSON_addChangeDelete($mMap, "our company.company sites[1].employee[0]", "Mark Marcusson") 56 | 57 | ; delete the second employee in the 2nd site ("Margret Margretson") 58 | _JSON_addChangeDelete($mMap, "our company.company sites[1].employee[1]") 59 | 60 | ; show the resulting data structure 61 | ConsoleWrite(_JSON_Generate($mMap) & @CRLF & @CRLF) 62 | ``` 63 | Strictly speaking, these functions should not even have "JSON" in their names, since they are generally applied to data structures in AutoIt. 64 | However, since they are often used in the JSON environment, we allow ourselves this small inaccuracy. 65 | 66 | ## Working with .json files 67 | Since questions about how to work with .json files come up from time to time, here is a brief explanation: 68 | 69 | The UDF functions read or output strings. It doesn't really matter where these come from. Therefore, the corresponding file functions should be used for JSON files: 70 | 71 | ```AutoIt 72 | ; read .json file and parse it to AutoIt datastructures 73 | $oData = _JSON_Parse(FileRead("C:\your\file\path.json")) 74 | 75 | ; serialize AutoIt datastructure into a .json file: 76 | FileDelete("C:\your\file\path.json") ; first empty the file 77 | FileWrite("C:\your\file\path.json", _JSON_Generate($oData)) 78 | ``` 79 | 80 | Direct editing of the JSON string does not take place. It is always a cascade of `.json --> AutoIt structure --> .json`. -------------------------------------------------------------------------------- /example.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "4434156", 4 | "url": "https://legacy.sky.com/v2/schedules/4434156", 5 | "title": "468_CORE_1_R.4 Schedule", 6 | "time_zone": "London", 7 | "start_at": "2017/08/10 19:00:00 +0100", 8 | "end_at": null, 9 | "notify_user": false, 10 | "delete_at_end": false, 11 | "executions": [], 12 | "recurring_days": [], 13 | "actions": [ 14 | { 15 | "type": "run", 16 | "offset": 0 17 | } 18 | ], 19 | "next_action_name": "run", 20 | "next_action_time": "2017/08/10 14:00:00 -0400", 21 | "user": { 22 | "id": "9604", 23 | "url": "https://legacy.sky.com/v2/users/9604", 24 | "login_name": "robin@ltree.com", 25 | "first_name": "Robin", 26 | "last_name": "John", 27 | "email": "robin@ltree.com", 28 | "role": "admin", 29 | "deleted": false 30 | }, 31 | "region": "EMEA", 32 | "can_edit": true, 33 | "vm_ids": null, 34 | "configuration_id": "19019196", 35 | "configuration_url": "https://legacy.sky.com/v2/configurations/19019196", 36 | "configuration_name": "468_CORE_1_R.4" 37 | }, 38 | { 39 | "id": "4444568", 40 | "url": "https://legacy.sky.com/v2/schedules/4444568", 41 | "title": "468_CORE_1_R.4 Schedule", 42 | "time_zone": "London", 43 | "start_at": "2017/08/11 12:00:00 +0100", 44 | "end_at": null, 45 | "notify_user": false, 46 | "delete_at_end": false, 47 | "executions": [], 48 | "recurring_days": [], 49 | "actions": [ 50 | { 51 | "type": "suspend", 52 | "offset": 0 53 | } 54 | ], 55 | "next_action_name": "suspend", 56 | "next_action_time": "2017/08/11 07:00:00 -0400", 57 | "user": { 58 | "id": "9604", 59 | "url": "https://legacy.sky.com/v2/users/9604", 60 | "login_name": "robin@ltree.com", 61 | "first_name": "Robin", 62 | "last_name": "John", 63 | "email": "robin@ltree.com", 64 | "role": "admin", 65 | "deleted": false 66 | }, 67 | "region": "EMEA", 68 | "can_edit": true, 69 | "vm_ids": null, 70 | "configuration_id": "19019196", 71 | "configuration_url": "https://legacy.sky.com/v2/configurations/19019196", 72 | "configuration_name": "468_CORE_1_R.4" 73 | } 74 | ] 75 | -------------------------------------------------------------------------------- /example.min.json: -------------------------------------------------------------------------------- 1 | [{"id":"4434156","url":"https://legacy.sky.com/v2/schedules/4434156","title":"468_CORE_1_R.4 Schedule","time_zone":"London","start_at":"2017/08/10 19:00:00 +0100","end_at":null,"notify_user":false,"delete_at_end":false,"executions":[],"recurring_days":[],"actions":[{"type":"run","offset":0}],"next_action_name":"run","next_action_time":"2017/08/10 14:00:00 -0400","user":{"id":"9604","url":"https://legacy.sky.com/v2/users/9604","login_name":"robin@ltree.com","first_name":"Robin","last_name":"John","email":"robin@ltree.com","role":"admin","deleted":false},"region":"EMEA","can_edit":true,"vm_ids":null,"configuration_id":"19019196","configuration_url":"https://legacy.sky.com/v2/configurations/19019196","configuration_name":"468_CORE_1_R.4"},{"id":"4444568","url":"https://legacy.sky.com/v2/schedules/4444568","title":"468_CORE_1_R.4 Schedule","time_zone":"London","start_at":"2017/08/11 12:00:00 +0100","end_at":null,"notify_user":false,"delete_at_end":false,"executions":[],"recurring_days":[],"actions":[{"type":"suspend","offset":0}],"next_action_name":"suspend","next_action_time":"2017/08/11 07:00:00 -0400","user":{"id":"9604","url":"https://legacy.sky.com/v2/users/9604","login_name":"robin@ltree.com","first_name":"Robin","last_name":"John","email":"robin@ltree.com","role":"admin","deleted":false},"region":"EMEA","can_edit":true,"vm_ids":null,"configuration_id":"19019196","configuration_url":"https://legacy.sky.com/v2/configurations/19019196","configuration_name":"468_CORE_1_R.4"}] 2 | --------------------------------------------------------------------------------