Leaderboard
Popular Content
Showing content with the highest reputation on 02/04/2023 in all areas
-
Introduction JSON (Javascript Object Notation) is a popular data-interchange format and supported by a lot of script languages. On AutoIt, there is already a >JSON UDF written by Gabriel Boehme. It is good but too slow, and not supports unicode and control characters very well. So I write a new one (and of course, fast one as usual). I use a machine code version of JSON parser called "jsmn". jsmn not only supports standard JSON, but also accepts some non-strict JSON string. See below for example. Important Update!! I rename the library from jsmn.au3 to json.au3. All function names are changed, too. Decoding Function Json_Decode($Json) $Json can be a standard or non-standard JSON string. For example, it accepts: { server: example.com port: 80 message: "this looks like a config file" } The most JSON data type will be decoded into corresponding AutoIt variable, including 1D array, string, number, true, false, and null. JSON object will be decoded into "Windows Scripting Dictionary Object" retuned from ObjCreate("Scripting.Dictionary"). AutoIt build-in functions like IsArray, IsBool, etc. can be used to check the returned data type. But for Object and Null, Json_IsObject() and Json_IsNull() should be used. If the input JSON string is invalid, @Error will be set to $JSMN_ERROR_INVAL. And if the input JSON string is not finish (maybe read from stream?), @Error will be set to $JSMN_ERROR_PART. Encoding Function Json_Encode($Data, $Option = 0, $Indent = "\t", $ArraySep = ",\r\n", $ObjectSep = ",\r\n", $ColonSep = ": ") $Data can be a string, number, bool, keyword(default or null), 1D arrry, or "Scripting.Dictionary" COM object. Ptr will be converted to number, Binary will be converted to string in UTF8 encoding. Other unsupported types like 2D array, dllstruct or object will be encoded into null. $Option is bitmask consisting following constant: $JSON_UNESCAPED_ASCII ; Don't escape ascii charcters between chr(1) ~ chr(0x1f) $JSON_UNESCAPED_UNICODE ; Encode multibyte Unicode characters literally $JSON_UNESCAPED_SLASHES ; Don't escape / $JSON_HEX_TAG ; All < and > are converted to \u003C and \u003E $JSON_HEX_AMP ; All &amp;amp;amp;s are converted to \u0026 $JSON_HEX_APOS ; All ' are converted to \u0027 $JSON_HEX_QUOT ; All " are converted to \u0022 $JSON_PRETTY_PRINT ; Use whitespace in returned data to format it $JSON_STRICT_PRINT ; Make sure returned JSON string is RFC4627 compliant $JSON_UNQUOTED_STRING ; Output unquoted string if possible (conflicting with $JSMN_STRICT_PRINT) Most encoding option have the same means like PHP's json_enocde() function. When $JSON_PRETTY_PRINT is set, output format can be change by other 4 parameters ($Indent, $ArraySep, $ObjectSep, and $ColonSep). Because these 4 output format parameters will be checked inside Jsmn_Encode() function, returned string will be always accepted by Jsmn_Decode(). $JSON_UNQUOTED_STRING can be used to output unquoted string that also accetped by Jsmn_Decode(). $JSON_STRICT_PRINT is used to check output format setting and avoid non-standard JSON output. So this option is conflicting with $JSON_UNQUOTED_STRING. Get and Put Functions Json_Put(ByRef $Var, $Notation, $Data, $CheckExists = False) Json_Get(ByRef $Var, $Notation) These functions helps user to access object or array more easily. Both dot notation and square bracket notation can be supported. Json_Put() by default will create non-exists objects and arrays. For example: Local $Obj Json_Put($Obj, ".foo", "foo") Json_Put($Obj, ".bar[0]", "bar") Json_Put($Obj, ".test[1].foo.bar[2].foo.bar", "Test") Local $Test = Json_Get($Obj, '["test"][1]["foo"]["bar"][2]["foo"]["bar"]') ; "Test" Object Help Functions Json_ObjCreate() Json_ObjPut(ByRef $Object, $Key, $Value) Json_ObjGet(ByRef $Object, $Key) Json_ObjDelete(ByRef $Object, $Key) Json_ObjExists(ByRef $Object, $Key) Json_ObjGetCount(ByRef $Object) Json_ObjGetKeys(ByRef $Object) Json_ObjClear(ByRef $Object) These functions are just warps of "Scripting.Dictionary" COM object. You can use these functions if you are not already familiar with it. == Update 2013/05/19 == * Add Jsmn_Encode() option "$JSMN_UNESCAPED_ASCII". Now the default output of Json_Encode() is exactly the same as PHP's json_encode() function (for example, chr(1) will be encoded into u0001). $JSON_UNESCAPED_ASCII ; Don't escape ascii charcters between chr(1) ~ chr(0x1f) == Update 2015/01/08 == * Rename the library from jsmn.au3 to json.au3. All function names are changed, too. * Add Json_Put() and Json_Get() * Add Null support * Using BinaryCall.au3 to loading the machine code. == Update 2018/01/13== (Jos) * Add JsonDump() to list all Json Keys and their values to easily figure out what they are. == Update 2018/10/01== (Jos) * Fixed JsonDump() some fields and values were not showing as discussed here - tnx @TheXman . == Update 2018/10/01b== (Jos) * Added Json_ObjGetItems, Tidied source and fixed au3check warnings - tnx @TheXman . == Update 2018/10/28== (Jos) * Added declaration for $value to avoid au3check warning - tnx @DerPensionist == Update 2018/12/16== (Jos) * Added another declaration for $value to avoid au3check warning and updated the version at the top - tnx @maniootek == Update 2018/12/29== (Jos) * Changed Json_ObjGet() and Json_ObjExists() to allow for multilevel object in string. == Update 2019/01/17== (Jos) * Added support for DOT notation in JSON functions. == Update 2019/07/15== (Jos) * Added support for reading keys with a dot inside when using a dot as separator (updated) == Update 2021/11/18== (TheXman) * Update details in below post: == Update 2021/11/20== (TheXman) * Minor RegEx update, no change to the functionality or result._Json(2021.11.20).zip1 point
-
@mistersquirrleI solved it! I will post soon a complete package including all needed functions to add a "Buy Now" feature in .au3 applications and to manage all activities up to the completion of the payment procedures.1 point
-
You have to save the file for the changes to take effect. You can't just ignore the warning when it tells you that the file you opened from the help file can't be saved. You need to save the updated script into a new location and then run it from there.1 point
-
Fix URL Split
Trong reacted to mistersquirrle for a topic
I think that a bit problem you're going to have with the data provided is that the Username contains a "@", and that's going to mess up parsing, since it's not expected until you've done username:password. If you HAVE to have the @ in the username, you may have to do a non-standard approach. Since you mentioned RegEx though, check out this example: https://regex101.com/r/cvP68Z/2 This is code from a comment on here: https://stackoverflow.com/questions/27745/getting-parts-of-a-url-regex The only problem with RegEx with AutoIt is I don't think that there's a way to get these named groups into the output, so using it is a bit difficult as the returning array can be different sizes, or have parts of the URL at different indexes. I've put together a not very clean looking regex method, it could probably be cleaned up to be nicer, but it seems like that it's currently slightly faster than your method (though both are under 1ms, so it doesn't really matter unless you're calling it a LOT). #include <Array.au3> #include <StringConstants.au3> #include <File.au3> Opt("MustDeclareVars", 1) Global Const $hWINHTTPDLL__WINHTTP = DllOpen("winhttp.dll") Global $httpUserName = "ftpuser@ftp.trong.live" Global $httpPassword = "Password" Global $sURL_Input = 'https://' & $httpUserName & ':' & $httpPassword & '@ftp.trong.live:4438/prv/test.jpg' Local Enum $eSplitParts_Scheme, $eSplitParts_Username, $eSplitParts_Password, $eSplitParts_Host, $eSplitParts_Port, $eSplitParts_Path, $eSplitParts_Query, $eSplitParts_Fragment, _ $eSplitParts_Max Local Enum $eSplitIndex_Values, $eSplitIndex_Name, $eSplitIndex_RegEx, _ $eSplitIndex_Max Local $hTimer Local $sURL_Protocol, $sURL_Protocol_Number, $sURL_Domain, $sURL_Port, $sURL_User, $sURL_Password, $sURL_FilePathName, $sURL_FilePATH, $sURL_FileName $hTimer = TimerInit() _WinHttp_SplitURL($sURL_Input, $sURL_Protocol, $sURL_Domain, $sURL_Port, $sURL_User, $sURL_Password, $sURL_FilePathName, $sURL_FilePATH, $sURL_FileName) ConsoleWrite("_WinHttp_SplitURL time: " & Round(TimerDiff($hTimer), 2) & 'ms' & @CRLF) ConsoleWrite("! URL_Input : " & $sURL_Input & @CRLF) ConsoleWrite("- Protocol : " & $sURL_Protocol & @CRLF) ConsoleWrite("- User : " & $sURL_User & @CRLF) ConsoleWrite("- Password : " & $sURL_Password & @CRLF) ConsoleWrite("- Domain : " & $sURL_Domain & @CRLF) ConsoleWrite("- Port : " & $sURL_Port & @CRLF) ConsoleWrite("- FilePathName: " & $sURL_FilePathName & @CRLF) ConsoleWrite("- FilePath : " & $sURL_FilePATH & @CRLF) ConsoleWrite("- FileName : " & $sURL_FileName & @CRLF) $hTimer = TimerInit() Local $aRegExParts = _WinHttp_SplitUrl_RegEx($sURL_Input) Local $sDrive = "", $sDir = "", $sFileName = "", $sExtension = "" Local $aPathSplit = _PathSplit($aRegExParts[$eSplitParts_Path][$eSplitIndex_Values], $sDrive, $sDir, $sFileName, $sExtension) ConsoleWrite("_WinHttp_SplitUrl_RegEx time: " & Round(TimerDiff($hTimer), 2) & 'ms' & @CRLF) ;~ _ArrayDisplay($aRegExParts) ConsoleWrite("! URL_Input : " & $sURL_Input & @CRLF) ConsoleWrite("- Protocol : " & $aRegExParts[$eSplitParts_Scheme][$eSplitIndex_Values] & @CRLF) ConsoleWrite("- User : " & $aRegExParts[$eSplitParts_Username][$eSplitIndex_Values] & @CRLF) ConsoleWrite("- Password : " & $aRegExParts[$eSplitParts_Password][$eSplitIndex_Values] & @CRLF) ConsoleWrite("- Domain : " & $aRegExParts[$eSplitParts_Host][$eSplitIndex_Values] & @CRLF) ConsoleWrite("- Port : " & $aRegExParts[$eSplitParts_Port][$eSplitIndex_Values] & @CRLF) ConsoleWrite("- FilePathName: " & $aRegExParts[$eSplitParts_Path][$eSplitIndex_Values] & @CRLF) ConsoleWrite("- FilePath : " & $sDir & @CRLF) ConsoleWrite("- FileName : " & $sFileName & $sExtension & @CRLF) ;~ _ArrayDisplay($aPathSplit) Func _WinHttp_SplitURL($sURL_Input, ByRef $sURL_Protocol, ByRef $sURL_Domain, ByRef $sURL_Port, ByRef $sURL_User, ByRef $sURL_Password, ByRef $sURL_FilePathName, ByRef $sURL_FilePATH, ByRef $sURL_FileName) If StringStripWS($sURL_Input, 8) = '' Then Return SetError(1, 0, 0) $sURL_Input = StringReplace($sURL_Input, "\", "/") If Not StringInStr($sURL_Input, '://') Then $sURL_Input = 'http://' & $sURL_Input __URL_Split($sURL_Input, $sURL_Protocol, $sURL_Domain, $sURL_FilePATH, $sURL_FileName) StringReplace($sURL_Input, '@', '') If @extended > 1 Then Local $sUser, $aUser = _StringBetween($sURL_Input, $sURL_Protocol & "://", '@') If IsArray($aUser) Then $sUser = $aUser[0] $sURL_Input = StringReplace($sURL_Input, $sURL_Protocol & "://" & $sUser & '@', $sURL_Protocol & "://" & $sUser & '%40') EndIf EndIf Local $aUrl = _WinHttpCrackUrl($sURL_Input) If IsArray($aUrl) Then $sURL_Protocol = ($aUrl[0] == '' ? $sURL_Protocol : $aUrl[0]) $sURL_Domain = ($aUrl[2] == '' ? $sURL_Domain : $aUrl[2]) $sURL_Port = $aUrl[3] $sURL_User = StringReplace($aUrl[4], '%40', '@') $sURL_Password = $aUrl[5] $sURL_FilePathName = ($aUrl[6] == '' ? $sURL_FilePATH & $sURL_FileName : $aUrl[6]) Return $aUrl EndIf Return SetError(1, 0, "") EndFunc ;==>_WinHttp_SplitURL Func _WinHttp_SplitUrl_RegEx($sURL) Local $aReturn[$eSplitParts_Max][$eSplitIndex_Max] ; https://regex101.com/r/cvP68Z/2 ; ^(?>(?<scheme>[^:\/?#]+):(?=\/\/))?(?>\/\/)?(?>(?>(?<login>[^:]+)(?::(?<password>[^@]+)?)?@)?(?<host>[^@\/?#:]*)(?::(?<port>\d+)?)?)?(?<path>[^?#]*)(?>\?(?<query>[^#]*))?(?>#(?<fragment>.*))? ;~ ConsoleWrite('$sURL: ' & $sURL & @CRLF) $aReturn[$eSplitParts_Scheme][$eSplitIndex_Name] = 'Scheme' $aReturn[$eSplitParts_Scheme][$eSplitIndex_RegEx] = _ '(^(?>(?<scheme>[^:\/?#]+):(?=\/\/))?(?>\/\/)?(?>(?>(?>[^:]+)(?::(?>[^@]+)?)?@)?(?>[^@\/?#:]*)(?::(?>\d+)?)?)?(?>[^?#]*)(?>\?(?>[^#]*))?(?>#(?>.*))?)' $aReturn[$eSplitParts_Username][$eSplitIndex_Name] = 'Username' $aReturn[$eSplitParts_Username][$eSplitIndex_RegEx] = _ '(^(?>(?>[^:\/?#]+):(?=\/\/))?(?>\/\/)?(?>(?>(?<login>[^:]+)(?::(?>[^@]+)?)?@)?(?>[^@\/?#:]*)(?::(?>\d+)?)?)?(?>[^?#]*)(?>\?(?>[^#]*))?(?>#(?>.*))?)' $aReturn[$eSplitParts_Password][$eSplitIndex_Name] = 'Password' $aReturn[$eSplitParts_Password][$eSplitIndex_RegEx] = _ '(^(?>(?>[^:\/?#]+):(?=\/\/))?(?>\/\/)?(?>(?>(?>[^:]+)(?::(?<password>[^@]+)?)?@)?(?>[^@\/?#:]*)(?::(?>\d+)?)?)?(?>[^?#]*)(?>\?(?>[^#]*))?(?>#(?>.*))?)' $aReturn[$eSplitParts_Host][$eSplitIndex_Name] = 'Host' $aReturn[$eSplitParts_Host][$eSplitIndex_RegEx] = _ '(^(?>(?>[^:\/?#]+):(?=\/\/))?(?>\/\/)?(?>(?>(?>[^:]+)(?::(?>[^@]+)?)?@)?(?<host>[^@\/?#:]*)(?::(?>\d+)?)?)?(?>[^?#]*)(?>\?(?>[^#]*))?(?>#(?>.*))?)' $aReturn[$eSplitParts_Port][$eSplitIndex_Name] = 'Port' $aReturn[$eSplitParts_Port][$eSplitIndex_RegEx] = _ '(^(?>(?>[^:\/?#]+):(?=\/\/))?(?>\/\/)?(?>(?>(?>[^:]+)(?::(?>[^@]+)?)?@)?(?>[^@\/?#:]*)(?::(?<port>\d+)?)?)?(?>[^?#]*)(?>\?(?>[^#]*))?(?>#(?>.*))?)' $aReturn[$eSplitParts_Path][$eSplitIndex_Name] = 'Path' $aReturn[$eSplitParts_Path][$eSplitIndex_RegEx] = _ '(^(?>(?>[^:\/?#]+):(?=\/\/))?(?>\/\/)?(?>(?>(?>[^:]+)(?::(?>[^@]+)?)?@)?(?>[^@\/?#:]*)(?::(?>\d+)?)?)?(?<path>[^?#]*)(?>\?(?>[^#]*))?(?>#(?>.*))?)' $aReturn[$eSplitParts_Query][$eSplitIndex_Name] = 'Query' $aReturn[$eSplitParts_Query][$eSplitIndex_RegEx] = _ '(^(?>(?>[^:\/?#]+):(?=\/\/))?(?>\/\/)?(?>(?>(?>[^:]+)(?::(?>[^@]+)?)?@)?(?>[^@\/?#:]*)(?::(?>\d+)?)?)?(?>[^?#]*)(?>\?(?<query>[^#]*))?(?>#(?>.*))?)' $aReturn[$eSplitParts_Fragment][$eSplitIndex_Name] = 'Fragment' $aReturn[$eSplitParts_Fragment][$eSplitIndex_RegEx] = _ '(^(?>(?>[^:\/?#]+):(?=\/\/))?(?>\/\/)?(?>(?>(?>[^:]+)(?::(?>[^@]+)?)?@)?(?>[^@\/?#:]*)(?::(?>\d+)?)?)?(?>[^?#]*)(?>\?(?>[^#]*))?(?>#(?<fragment>.*))?)' Local $aParts For $iPart = 0 To $eSplitParts_Max - 1 $aParts = StringRegExp($sURL, $aReturn[$iPart][$eSplitIndex_RegEx], $STR_REGEXPARRAYGLOBALMATCH) If @error Then ConsoleWrite('StringRegExp error: ' & @error & @CRLF) ContinueLoop Else ; For some reason when there's NO match, it's returning the full input in [0], so the pattern just defaults to matching the whole thing, meaning what we want is in [1], if something was found For $i = 1 To UBound($aParts) - 1 $aReturn[$iPart][$eSplitIndex_Values] = $aParts[$i] ;~ ConsoleWrite($aReturn[$iPart][$eSplitIndex_Name] & ' (' & $i & '): ' & $aParts[$i] & @CRLF) Next ;~ _ArrayDisplay($aParts) EndIf Next ;~ _ArrayDisplay($aReturn) Return $aReturn EndFunc ;==>_WinHttp_SplitUrl_RegEx Func __URL_Split($sURL_Input, ByRef $sURL_Protocol, ByRef $sURL_Domain, ByRef $sURL_FilePATH, ByRef $sURL_FileName) Local $sURL_Protocol_Number_Pattern = '^(?s)(?i)(http|ftp|sftp|ftps|https|file)://(.*?/|.*$)(.*/){0,}(.*)$' Local $sURL_Pattern = "^(?i)(?:(?:[a-z]+):\/\/)?" & "(?:(?:(?:[^@:]+))" & "(?::(?:[^@]+))?@)?" & "([^\/:]+)" & "(?::(?:\d+))?" & "(?:\/(?:[^?]+)?)?" & "(?:\?\N+)?" Local $aURL_Pattern = StringRegExp($sURL_Input, $sURL_Protocol_Number_Pattern, 2) If Not IsArray($aURL_Pattern) Or UBound($aURL_Pattern) - 1 <> 4 Then Return SetError(1, 0, 0) If StringRight($aURL_Pattern[2], 1) = '/' Then $aURL_Pattern[2] = StringTrimRight($aURL_Pattern[2], 1) $aURL_Pattern[3] = '/' & $aURL_Pattern[3] EndIf $sURL_Protocol = $aURL_Pattern[1] Local $aHost = StringRegExp($sURL_Input, $sURL_Pattern, 1) If Not @error And IsArray($aHost) Then $sURL_Domain = $aHost[0] Else $sURL_Domain = $aURL_Pattern[2] EndIf $sURL_FilePATH = $aURL_Pattern[3] $sURL_FileName = $aURL_Pattern[4] Return $aURL_Pattern EndFunc ;==>__URL_Split Func _WinHttpCrackUrl($sURL, $iFlag = Default) __WinHttpDefault($iFlag, 0x80000000) Local $tURL_COMPONENTS = DllStructCreate("dword StructSize;" & "ptr SchemeName;" & "dword SchemeNameLength;" & "int Scheme;" & "ptr HostName;" & "dword HostNameLength;" & "word Port;" & "ptr UserName;" & "dword UserNameLength;" & "ptr Password;" & "dword PasswordLength;" & "ptr UrlPath;" & "dword UrlPathLength;" & "ptr ExtraInfo;" & "dword ExtraInfoLength") DllStructSetData($tURL_COMPONENTS, 1, DllStructGetSize($tURL_COMPONENTS)) Local $tBuffers[6] Local $iURLLen = StringLen($sURL) For $i = 0 To 5 $tBuffers[$i] = DllStructCreate("wchar[" & $iURLLen + 1 & "]") Next DllStructSetData($tURL_COMPONENTS, "SchemeNameLength", $iURLLen) DllStructSetData($tURL_COMPONENTS, "SchemeName", DllStructGetPtr($tBuffers[0])) DllStructSetData($tURL_COMPONENTS, "HostNameLength", $iURLLen) DllStructSetData($tURL_COMPONENTS, "HostName", DllStructGetPtr($tBuffers[1])) DllStructSetData($tURL_COMPONENTS, "UserNameLength", $iURLLen) DllStructSetData($tURL_COMPONENTS, "UserName", DllStructGetPtr($tBuffers[2])) DllStructSetData($tURL_COMPONENTS, "PasswordLength", $iURLLen) DllStructSetData($tURL_COMPONENTS, "Password", DllStructGetPtr($tBuffers[3])) DllStructSetData($tURL_COMPONENTS, "UrlPathLength", $iURLLen) DllStructSetData($tURL_COMPONENTS, "UrlPath", DllStructGetPtr($tBuffers[4])) DllStructSetData($tURL_COMPONENTS, "ExtraInfoLength", $iURLLen) DllStructSetData($tURL_COMPONENTS, "ExtraInfo", DllStructGetPtr($tBuffers[5])) Local $aCall = DllCall($hWINHTTPDLL__WINHTTP, "bool", "WinHttpCrackUrl", "wstr", $sURL, "dword", $iURLLen, "dword", $iFlag, "struct*", $tURL_COMPONENTS) If @error Or Not $aCall[0] Then Return SetError(1, 0, 0) Local $aRet[8] = [DllStructGetData($tBuffers[0], 1), DllStructGetData($tURL_COMPONENTS, "Scheme"), DllStructGetData($tBuffers[1], 1), DllStructGetData($tURL_COMPONENTS, "Port"), DllStructGetData($tBuffers[2], 1), DllStructGetData($tBuffers[3], 1), DllStructGetData($tBuffers[4], 1), DllStructGetData($tBuffers[5], 1)] Return $aRet EndFunc ;==>_WinHttpCrackUrl Func __WinHttpDefault(ByRef $vInput, $vOutput) If $vInput = Default Or Number($vInput) = -1 Then $vInput = $vOutput EndFunc ;==>__WinHttpDefault Func _StringBetween($sString, $sStart, $sEnd, $iMode = 0, $bCase = False) $sStart = $sStart ? "\Q" & $sStart & "\E" : "\A" If $iMode <> 1 Then $iMode = 0 If $iMode = 0 Then $sEnd = $sEnd ? "(?=\Q" & $sEnd & "\E)" : "\z" Else $sEnd = $sEnd ? "\Q" & $sEnd & "\E" : "\z" EndIf If $bCase = Default Then $bCase = False EndIf Local $aRet = StringRegExp($sString, "(?s" & (Not $bCase ? "i" : "") & ")" & $sStart & "(.*?)" & $sEnd, 3) If @error Then Return SetError(1, 0, 0) Return $aRet EndFunc ;==>_StringBetween Output: _WinHttp_SplitURL time: 0.5ms ! URL_Input : https://ftpuser@ftp.trong.live:Password@ftp.trong.live:4438/prv/test.jpg - Protocol : https - User : ftpuser@ftp.trong.live - Password : Password - Domain : ftp.trong.live - Port : 4438 - FilePathName: /prv/test.jpg - FilePath : /prv/ - FileName : test.jpg _WinHttp_SplitUrl_RegEx time: 0.24ms ! URL_Input : https://ftpuser@ftp.trong.live:Password@ftp.trong.live:4438/prv/test.jpg - Protocol : https - User : ftpuser@ftp.trong.live - Password : Password - Domain : ftp.trong.live - Port : 4438 - FilePathName: /prv/test.jpg - FilePath : /prv/ - FileName : test.jpg1 point