101

Re: AHK: Класс для работы с JSON

teadrinker пишет:

Не доверяешь — не запускай. Я постоянно пользуюсь JSON только из Гугл-транслейта, и то его не запускаю, а разбираю парсингом.

Если ты выполняешь eval, то тем самым ты его уже запускаешь.

teadrinker пишет:

За весь свой опыт не помню ситуации, чтобы был риск запустить из JSON вредоносный код. Можешь привести пример таковой?

Я могу привести пример как запустить ActiveXObject из json.
Создастся test.txt на диске D.
В IE нужно разрешить initialize and script Active X controls.

json = {"key": "value"});var fso = new ActiveXObject('Scripting.FileSystemObject');fso.CreateTextFile('D:\\test.txt');({}
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=EDGE"">")
JS := doc.parentWindow
JS.eval("(" json ")")

Возможно есть варианты написания серьезных эксплойтов, но это надо интересоваться на соответствующих форумах.
Наверное стоит у Xameleon спросить примеры - может он в курсе.

102

Re: AHK: Класс для работы с JSON

Malcev пишет:

Если ты выполняешь eval, то тем самым ты его уже запускаешь

Да, всё правильно. Я почему-то подумал, что там eval нету, но был не прав. Так это как раз тот случай, когда я источнику доверяю на 100%.

Malcev пишет:

Я могу привести пример как запустить ActiveXObject из json.

Так значит имеющегося

JS.("delete ActiveXObject; delete GetObject;")

достаточно?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

103

Re: AHK: Класс для работы с JSON

Вот не знаю, спрошу у Xameleona, какие в принципе могут быть варианты эксплойта.

104 (изменено: teadrinker, 2021-02-10 03:48:24)

Re: AHK: Класс для работы с JSON

Malcev пишет:

Я могу привести пример как запустить ActiveXObject из json.

Собственно, я имел в виду не как запустить код технически, а конкретную ситуацию, когда этот код мог бы у меня появиться.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

105

Re: AHK: Класс для работы с JSON

Malcev пишет:

может просто достаточно удалить объекты из json перед выполнением eval?

Нет, мы там не из json объекты удаляем, а из запускающего его js-объекта.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

106

Re: AHK: Класс для работы с JSON

teadrinker пишет:

Собственно, я имел в виду не как запустить код технически, а конкретную ситуацию, когда этот код мог бы у меня появиться.

Ну, например, сайт с которого получаем json поймал вирус.

teadrinker пишет:

Нет, мы там не из json объекты удаляем, а из запускающего его js-объекта.

А, сейчас поправлю.

107

Re: AHK: Класс для работы с JSON

Malcev пишет:

Ну, например, сайт с которого получаем json поймал вирус.

Да, наверно возможно.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

108

Re: AHK: Класс для работы с JSON

Собственно, от eval можно избавиться, вместо

JS.eval("(" . json . ")")

можно использовать

JS.JSON.parse(json)
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

109

Re: AHK: Класс для работы с JSON

Наверное это лучший вариант.

110

Re: AHK: Класс для работы с JSON

Правда тут надо еще будет скорости сравнить.

111

Re: AHK: Класс для работы с JSON

Сравнил, примерно одинаково, с parse чуть быстрее:

#NoEnv
SetBatchLines, -1

url := "https://addons-ecs.forgesvc.net/api/v2/addon/8025/files"

whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.Open("GET", url, false)
whr.Send()
arr := whr.responseBody
pData := NumGet(ComObjValue(arr) + 8 + A_PtrSize)
MsgBox, % "json size: " . (length := arr.MaxIndex() + 1)//1024 . " kB"
MsgBox, % json := StrGet(pData, length, "utf-8")

for k, v in ["parse", "eval"] {
   start := A_TickCount
   Obj_%v% := JsonToAHK_%v%(json)
   time_%v% := A_TickCount - start
}
MsgBox, % "parse: " . time_parse . "`neval: " . time_eval

JsonToAHK_parse(json, rec := false) {
   static doc := ComObjCreate("htmlfile")
         , __ := doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         , JS := doc.parentWindow
   if !rec
      obj := %A_ThisFunc%(JS.JSON.parse(json), true)
   else if !IsObject(json)
      obj := json
   else if JS.Object.prototype.toString.call(json) == "[object Array]" {
      obj := []
      Loop % json.length
         obj.Push( %A_ThisFunc%(json[A_Index - 1], true) )
   }
   else {
      obj := {}
      keys := JS.Object.keys(json)
      Loop % keys.length {
         k := keys[A_Index - 1]
         obj[k] := %A_ThisFunc%(json[k], true)
      }
   }
   Return obj
}

JsonToAHK_eval(json, rec := false) {
   static doc := ComObjCreate("htmlfile")
         , __ := doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         , JS := doc.parentWindow
   if !rec
      obj := %A_ThisFunc%(JS.eval("(" . json . ")"), true)
   else if !IsObject(json)
      obj := json
   else if JS.Object.prototype.toString.call(json) == "[object Array]" {
      obj := []
      Loop % json.length
         obj.Push( %A_ThisFunc%(json[A_Index - 1], true) )
   }
   else {
      obj := {}
      keys := JS.Object.keys(json)
      Loop % keys.length {
         k := keys[A_Index - 1]
         obj[k] := %A_ThisFunc%(json[k], true)
      }
   }
   Return obj
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

112

Re: AHK: Класс для работы с JSON

teadrinker Добрый день. Подскажите, пож-та, хочу вытащить "documentId", но работает если только удалить скобки "[]"


jsonStr = {"response":{"pageSize":20,"docs":[{"id":"ISOGD_7815feb6-1831-4b3b-b643-eb565c8152c3","type":"ISOGD","documentId":"7815feb6-1831-4b3b-b643-eb565c8152c3","reestr":"8150","documentType":"PKR","documentDate":"22.04.2021","documentNumber":"21/00904","areaNames":["ЦАО"],"districtNames":["Хамовники"],"objectAddress":"Пречистенка ул., д.14/1, стр.2","objectName":"Учрежденческое здание","documentName":"Паспорт колористического решения","registrationNumber":"45383000-08-369569","registrationDate":"22.04.2021","cadastralNumbers":["77:01:0001049:1008"],"documentStatusCode":"ACTIVE","documentRegistrationCode":"REGISTERED","organizationName":"ГОСУДАРСТВЕННОЕ ","organizationRoleCode":"ZAK","insertedDate":"22.04.2021 10:57:28","operatorFio":"Смирнов Александр Алексеевич","documentSource":"2","documentOrganizationCode":"MKA","alfFolderId":"0ed711a0-ed93-41b7-8978-8048ca2c0972","registeredFIO":"Пархомик Андрей Валерьевич","updatedDate":"23.04.2021 17:09:27","title":"№ 21/00904  от 22.04.2021 Пречистенка ул., д.14/1, стр.2 (ЦАО, Хамовники)"}]},"highlighting":{"map":{"ISOGD_7815feb6-1831-4b3b-b643-eb565c8152c3":{"cadastralNumbers":["<b>77:01:0001049:1008</b>"]}}}}

obj := JSON.Parse(jsonStr)
MsgBox, % obj.response.docs.documentId
return

	

 
class JSON
{
   static JS := JSON._GetJScripObject()
   
   Parse(JsonString)  {
      try oJSON := this.JS.("(" JsonString ")")
      catch  {
         MsgBox, Wrong JsonString!
         Return
      }
      Return this._CreateObject(oJSON)
   }
   
   Stringify(obj)  {
      if IsObject( obj )  {
         isArray := true
         for key in obj
            if !( key = A_Index || isArray := false )
               break
            
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : this.Stringify(k) . ":" ) . this.Stringify(v)

         return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         return obj
      
      for k, v in [["\", "\\"], [A_Tab, "\t"], ["""", "\"""], ["/", "\/"], ["`n", "\n"], ["`r", "\r"], [Chr(12), "\f"], [Chr(08), "\b"]]
         obj := StrReplace( obj, v[1], v[2] )
      
      while RegexMatch( obj, "[^\x20-\x7e]", key )  {
         str := Asc( key )
         val := "\u" . Chr( ( ( str >> 12 ) & 15 ) + ( ( ( str >> 12 ) & 15 ) < 10 ? 48 : 55 ) )
               . Chr( ( ( str >> 8 ) & 15 ) + ( ( ( str >> 8 ) & 15 ) < 10 ? 48 : 55 ) )
               . Chr( ( ( str >> 4 ) & 15 ) + ( ( ( str >> 4 ) & 15 ) < 10 ? 48 : 55 ) )
               . Chr( ( str & 15 ) + ( ( str & 15 ) < 10 ? 48 : 55 ) )
         obj := StrReplace(obj, key, val)
      }
      Return """" obj """"
   }
   
   GetFromUrl(url, contentType := "", userAgent := "", body := "")  {
      ; в случае удачи будет возвращена JSON-строка, в случае ошибки — массив с одним элементом-строкой с описанием ошибки
      try  {
         XmlHttp := ComObjCreate("Microsoft.XmlHttp")
         XmlHttp.Open("GET", url, false)
         ( contentType && XmlHttp.SetRequestHeader("Content-Type", contentType) )
         ( userAgent && XmlHttp.SetRequestHeader("User-Agent", userAgent) )
         XmlHttp.Send(body)
      }
      catch e
         Return ["Error!`n" . e.Message]
      status := XmlHttp.Status
      Return status = 200 ? XmlHttp.ResponseText : ["Error! Status: " . status . ", ResponseText: " . XmlHttp.ResponseText]
   }

   _GetJScripObject()  {
      VarSetCapacity(tmpFile, ((MAX_PATH := 260) - 14) << !!A_IsUnicode, 0)
      DllCall("GetTempFileName", Str, A_Temp, Str, "AHK", UInt, 0, Str, tmpFile)
      
      FileAppend,
      (
      <component>
      <public><method name='eval'/></public>
      <script language='JScript'></script>
      </component>
      ), % tmpFile
      
      JS := ObjBindMethod( ComObjGet("script:" . tmpFile), "eval" )
      FileDelete, % tmpFile
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS)  {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.("delete ActiveXObject; delete GetObject;")
      JS.(JScript)
   }

   _CreateObject(ObjJS)  {
      res := ObjJS.IsArray()
      if (res = "")
         Return ObjJS
      
      else if (res = -1)  {
         obj := []
         Loop % ObjJS.length
            obj[A_Index] := this._CreateObject(ObjJS[A_Index - 1])
      }
      else if (res = 0)  {
         obj := {}
         keys := ObjJS.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(ObjJS[k])
      }
      Return obj
   }
}

)


113

Re: AHK: Класс для работы с JSON

А как вытащить test из такого объекта

Obj := ["test"]

?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

114

Re: AHK: Класс для работы с JSON

teadrinker

Cпасибо!

MsgBox, % obj.response.docs[1].documentId

115

Re: AHK: Класс для работы с JSON

teadrinker А как узнать количество файлов? В данном примере 3 ("obj.files.length" не работает)


FileURL := ""

HTTP.ResponseText = 
{"folderId":"bf6530be-cf29-438b-82c0-a4546a0466ef","folderName":"45383000-08-369567","files":[{"fileId":"91b2a1ba-1a6e-43ba-866b-c7cd491055fc","fileName":"COLOURPASS.xml","fileDate":"2021-04-22T11:00:45.82","contentType":"application/xml","contentLength":5133,"signed":false},{"fileId":"ef07f2af-b165-4a0f-80ef-cb29d4248d03","fileName":"Паспорт колористического решения_ЦАО, Хамовники, Пречистенка ул., д. 14_1, с. 3_21_00905.pdf","fileDate":"2021-04-22T11:00:51.723","contentType":"application/pdf","contentLength":12964301,"signed":false},{"fileId":"86a028fe-2e63-4f27-9ff8-46e9414b1a98","fileName":"Протокол регистрации 369567.xml","fileDate":"2021-04-22T12:27:19.237","contentType":"application/xml","contentLength":459,"signed":false}],"hidden":false}

obj := JSON.Parse(HTTP.ResponseText)

loop 3 
{
  if InStr(obj.files[A_index].fileName, "COLOURPASS")
     UrlDownloadToFile, % FileURL "/" obj.files[A_index].fileId, % "C:\Отчёт\" obj.files[A_index].fileName
  if InStr(obj.files[A_index].fileName, "Паспорт")
    UrlDownloadToFile, % FileURL "/" obj.files[A_index].fileId, % "C:\Отчёт\" obj.files[A_index].fileName
}

116

Re: AHK: Класс для работы с JSON

Не вижу, как эти вопросы связаны с JSON. Преобразование JSON в AHK-объект ведь проблем не вызывает? А если объект уже создан, то без разницы, каким образом он был получен, он поддерживает те же свойства и методы, что и любой обычный AHK-объект. Поэтому, я бы начал с того, что заглянул бы в справку по свойствам и методам объектов, там бы узнал правильный синтаксис.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

117

Re: AHK: Класс для работы с JSON

Malcev пишет:

Нет поддержки int64:

val = {"a": 17849713558010739}
MsgBox, % JSON.Parse(val).a

В этой библиотеке есть:
https://github.com/G33kDude/cJson.ahk

118

Re: AHK: Класс для работы с JSON

teadrinker, а ты своей json библиотекой пользуешься в chrome.ahk?

119

Re: AHK: Класс для работы с JSON

Да, но не этой, а LightJson.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

120

Re: AHK: Класс для работы с JSON

Можешь скинуть его сюда вместе с chrome.ahk.
Мне кажется там баг.

121

Re: AHK: Класс для работы с JSON

class Chrome
{
	static DebugPort := 9222
	
	/*
		Escape a string in a manner suitable for command line parameters
	*/
	CliEscape(Param)
	{
		return """" RegExReplace(Param, "(\\*)""", "$1$1\""") """"
	}
	
	FindInstance(exeName)
	{
		static Needle := "i)\Q" . exeName . "\E""?\s+--remote-debugging-port=(\d+)"
		Out := {}
      for k, PID in this.EnumProcessesByName(exeName)
         cmd := this.GetCommandLine(PID).cmd
      until found := RegExMatch(cmd, Needle, Match)
      Return found ? {port: Match1, PID: PID} : ""
	}
   
   EnumProcessesByName(procName) {
      local
      if !DllCall("Wtsapi32\WTSEnumerateProcesses", Ptr, 0, UInt, 0, UInt, 1, PtrP, pProcessInfo, PtrP, count)
         throw Exception("WTSEnumerateProcesses failed. A_LastError: " . A_LastError)
      
      addr := pProcessInfo, PIDs := []
      Loop % count  {
         if StrGet( NumGet(addr + 8) ) = procName
            PID := NumGet(addr + 4, "UInt"), PIDs.Push(PID)
         addr += A_PtrSize = 4 ? 16 : 24
      }
      DllCall("Wtsapi32\WTSFreeMemory", Ptr, pProcessInfo)
      Return PIDs
   }
   
   GetCommandLine(PID, GetImagePath := false) {
      local
      static SetDebug := 0, PROCESS_QUERY_INFORMATION := 0x400, PROCESS_VM_READ := 0x10, STATUS_SUCCESS := 0
      hProc := DllCall("OpenProcess", UInt, PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, Int, 0, UInt, PID, Ptr)
      (A_Is64bitOS && DllCall("IsWow64Process", Ptr, hProc, UIntP, IsWow64))
      if (!A_Is64bitOS || IsWow64)
         PtrSize := 4, PtrType := "UInt", pPtr := "UIntP", offsetCMD := 0x40
      else
         PtrSize := 8, PtrType := "Int64", pPtr := "Int64P", offsetCMD := 0x70

      hModule := DllCall("GetModuleHandle", "str", "Ntdll", Ptr)
      if (A_PtrSize < PtrSize)  {            ; скрипт 32, целевой процесс 64
         if !QueryInformationProcess := DllCall("GetProcAddress", Ptr, hModule, AStr, "NtWow64QueryInformationProcess64", Ptr)
            failed := "NtWow64QueryInformationProcess64"
         if !ReadProcessMemory := DllCall("GetProcAddress", Ptr, hModule, AStr, "NtWow64ReadVirtualMemory64", Ptr)
            failed := "NtWow64ReadVirtualMemory64"
         info := 0, szPBI := 48, offsetPEB := 8
      }
      else  {
         if !QueryInformationProcess := DllCall("GetProcAddress", Ptr, hModule, AStr, "NtQueryInformationProcess", Ptr)
            failed := "NtQueryInformationProcess"
         ReadProcessMemory := "ReadProcessMemory"
         if (A_PtrSize > PtrSize)            ; скрипт 64, целевой процесс 32
            info := 26, szPBI := 8, offsetPEB := 0
         else                                ; скрипт и целевой процесс одной битности
            info := 0, szPBI := PtrSize * 6, offsetPEB := PtrSize
      }
      if failed  {
         DllCall("CloseHandle", Ptr, hProc)
         MsgBox, Не удалось получить указатель на функцию %failed%
         Return
      }
      VarSetCapacity(PBI, 48, 0)
      if DllCall(QueryInformationProcess, Ptr, hProc, UInt, info, Ptr, &PBI, UInt, szPBI, UIntP, bytes) != STATUS_SUCCESS  {
         DllCall("CloseHandle", Ptr, hProc)
         Return
      }
      pPEB := NumGet(&PBI + offsetPEB, PtrType)
      DllCall(ReadProcessMemory, Ptr, hProc, PtrType, pPEB + PtrSize * 4, pPtr, pRUPP, PtrType, PtrSize, UIntP, bytes)
      DllCall(ReadProcessMemory, Ptr, hProc, PtrType, pRUPP + offsetCMD, UShortP, szCMD, PtrType, 2, UIntP, bytes)
      DllCall(ReadProcessMemory, Ptr, hProc, PtrType, pRUPP + offsetCMD + PtrSize, pPtr, pCMD, PtrType, PtrSize, UIntP, bytes)
      
      VarSetCapacity(buff, szCMD, 0)
      DllCall(ReadProcessMemory, Ptr, hProc, PtrType, pCMD, Ptr, &buff, PtrType, szCMD, UIntP, bytes)
      obj := { cmd: StrGet(&buff, "UTF-16") }
      
      if (GetImagePath && obj.cmd)  {
         DllCall(ReadProcessMemory, Ptr, hProc, PtrType, pRUPP + offsetCMD - PtrSize*2, UShortP, szPATH, PtrType, 2, UIntP, bytes)
         DllCall(ReadProcessMemory, Ptr, hProc, PtrType, pRUPP + offsetCMD - PtrSize, pPtr, pPATH, PtrType, PtrSize, UIntP, bytes)
         
         VarSetCapacity(buff, szPATH, 0)
         DllCall(ReadProcessMemory, Ptr, hProc, PtrType, pPATH, Ptr, &buff, PtrType, szPATH, UIntP, bytes)
         obj.path := StrGet(&buff, "UTF-16") . (IsWow64 ? " *32" : "")
      }
      DllCall("CloseHandle", Ptr, hProc)
      Return obj
   }
   
	__New(ProfilePath:="", URLs:="about:blank", Flags:="", ChromePath:="", DebugPort:="")
	{
		; Verify ProfilePath
		if (ProfilePath != "" && !InStr(FileExist(ProfilePath), "D"))
			throw Exception("The given ProfilePath does not exist")
		this.ProfilePath := ProfilePath
		
		; Verify ChromePath
		if (ChromePath == "")
			FileGetShortcut, %A_StartMenuCommon%\Programs\Google Chrome.lnk, ChromePath
		if (ChromePath == "")
			RegRead, ChromePath, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe
		if !FileExist(ChromePath)
			throw Exception("Chrome could not be found")
		this.ChromePath := ChromePath
		
		; Verify DebugPort
		if (DebugPort != "")
		{
			if DebugPort is not integer
				throw Exception("DebugPort must be a positive integer")
			else if (DebugPort <= 0)
				throw Exception("DebugPort must be a positive integer")
			this.DebugPort := DebugPort
		}
		
		; Escape the URL(s)
		for Index, URL in IsObject(URLs) ? URLs : [URLs]
			URLString .= " " this.CliEscape(URL)
		
		Run, % this.CliEscape(ChromePath)
		. " --remote-debugging-port=" this.DebugPort
		. (ProfilePath ? " --user-data-dir=" this.CliEscape(ProfilePath) : "")
		. (Flags ? " " Flags : "")
		. URLString
		,,, OutputVarPID
		this.PID := OutputVarPID
	}
	
	/*
		End Chrome by terminating the process.
	*/
	Kill()
	{
		Process, Close, % this.PID
	}
	
	/*
		Queries chrome for a list of pages that expose a debug interface.
		In addition to standard tabs, these include pages such as extension
		configuration pages.
	*/
	GetPageList()
	{
		http := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		StartTime := A_TickCount
		while (A_TickCount-StartTime < 30000)
		{
			try
			{
				http.Open("GET", "http://127.0.0.1:" this.DebugPort "/json/list", true)
				http.Send()
				http.WaitForResponse(-1)
				if (http.Status = 200)
					break
			}
			Sleep, 50
		}
		return LightJson.Parse(http.responseText)
	}
	
	/*
		Returns a connection to the debug interface of a page that matches the
		provided criteria. When multiple pages match the criteria, they appear
		ordered by how recently the pages were opened.
		
		Key        - The key from the page list to search for, such as "url" or "title"
		Value      - The value to search for in the provided key
		MatchMode  - What kind of search to use, such as "exact", "contains", "startswith", or "regex"
		Index      - If multiple pages match the given criteria, which one of them to return
		fnCallback - A function to be called whenever message is received from the page
	*/
	GetPageBy(Key, Value, MatchMode:="exact", Index:=1, fnCallback:="")
	{
		Count := 0
		for n, PageData in this.GetPageList()
		{
			if (((MatchMode = "exact" && PageData[Key] = Value) ; Case insensitive
				|| (MatchMode = "contains" && InStr(PageData[Key], Value))
				|| (MatchMode = "startswith" && InStr(PageData[Key], Value) == 1)
				|| (MatchMode = "regex" && PageData[Key] ~= Value))
				&& ++Count == Index)
				return new this.Page(PageData.webSocketDebuggerUrl, fnCallback)
		}
	}
	
	/*
		Shorthand for GetPageBy("url", Value, "startswith")
	*/
	GetPageByURL(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
	{
		return this.GetPageBy("url", Value, MatchMode, Index, fnCallback)
	}
	
	/*
		Shorthand for GetPageBy("title", Value, "startswith")
	*/
	GetPageByTitle(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
	{
		return this.GetPageBy("title", Value, MatchMode, Index, fnCallback)
	}
	
	/*
		Shorthand for GetPageBy("type", Type, "exact")
		
		The default type to search for is "page", which is the visible area of
		a normal Chrome tab.
	*/
	GetPage(Index:=1, Type:="page", fnCallback:="")
	{
		return this.GetPageBy("type", Type, "exact", Index, fnCallback)
	}
	
	/*
		Connects to the debug interface of a page given its WebSocket URL.
	*/
	class Page
	{
		Connected := False
		ID := 0
		Responses := []
		
		/*
			wsurl      - The desired page's WebSocket URL
			fnCallback - A function to be called whenever message is received
		*/
		__New(wsurl, fnCallback:="")
		{
			this.fnCallback := fnCallback
			this.BoundKeepAlive := this.Call.Bind(this, "Browser.getVersion",, False)
			
			; TODO: Throw exception on invalid objects
			if IsObject(wsurl)
				wsurl := wsurl.webSocketDebuggerUrl
			
			wsurl := StrReplace(wsurl, "localhost", "127.0.0.1")
			this.ws := {"base": this.WebSocket, "_Event": this.Event, "Parent": this}
			this.ws.__New(wsurl)
			
			while !this.Connected
				Sleep, 50
		}
		
		/*
			Calls the specified endpoint and provides it with the given
			parameters.
			
			DomainAndMethod - The endpoint domain and method name for the
			endpoint you would like to call. For example:
			PageInst.Call("Browser.close")
			PageInst.Call("Schema.getDomains")
			
			Params - An associative array of parameters to be provided to the
			endpoint. For example:
			PageInst.Call("Page.printToPDF", {"scale": 0.5 ; Numeric Value
			, "landscape": LightJson.true ; Boolean Value
			, "pageRanges: "1-5, 8, 11-13"}) ; String value
			PageInst.Call("Page.navigate", {"url": "https://autohotkey.com/"})
			
			WaitForResponse - Whether to block until a response is received from
			Chrome, which is necessary to receive a return value, or whether
			to continue on with the script without waiting for a response.
		*/
		Call(DomainAndMethod, Params:="", WaitForResponse:=True)
		{
			if !this.Connected
				throw Exception("Not connected to tab")
			
			; Use a temporary variable for ID in case more calls are made
			; before we receive a response.
			ID := this.ID += 1
			this.ws.Send(LightJson.Stringify({"id": ID
			, "params": Params ? Params : {}
			, "method": DomainAndMethod}))
			
			if !WaitForResponse
				return
			
			; Wait for the response
			this.responses[ID] := False
			while !this.responses[ID]
				Sleep, 50
			
			; Get the response, check if it's an error
			response := this.responses.Delete(ID)
			if (response.error)
				throw Exception("Chrome indicated error in response",, LightJson.Stringify(response.error))
			
			return response.result
		}
		
		/*
			Run some JavaScript on the page. For example:
			
			PageInst.Evaluate("alert(""I can't believe it's not IE!"");")
			PageInst.Evaluate("document.getElementsByTagName('button')[0].click();")
		*/
		Evaluate(JS)
		{
			response := this.Call("Runtime.evaluate",
			( LTrim Join
			{
				"expression": JS,
				"objectGroup": "console",
				"includeCommandLineAPI": LightJson.true,
				"silent": LightJson.false,
				"returnByValue": LightJson.false,
				"userGesture": LightJson.true,
				"awaitPromise": LightJson.false
			}
			))
			
			if (response.exceptionDetails)
				throw Exception(response.result.description,, LightJson.Stringify(response.exceptionDetails))
			
			return response.result
		}
		
		/*
			Waits for the page's readyState to match the DesiredState.
			
			DesiredState - The state to wait for the page's ReadyState to match
			Interval     - How often it should check whether the state matches
		*/
		WaitForLoad(DesiredState:="complete", Interval:=100)
		{
			while this.Evaluate("document.readyState").value != DesiredState
				Sleep, Interval
		}
		
		/*
			Internal function triggered when the script receives a message on
			the WebSocket connected to the page.
		*/
		Event(EventName, Event)
		{
			; If it was called from the WebSocket adjust the class context
			if this.Parent
				this := this.Parent
			
			; TODO: Handle Error events
			if (EventName == "Open")
			{
				this.Connected := True
				BoundKeepAlive := this.BoundKeepAlive
				SetTimer, %BoundKeepAlive%, 15000
			}
			else if (EventName == "Message")
			{
				data := LightJson.Parse(Event.data)
				
				; Run the callback routine
				fnCallback := this.fnCallback
				if (newData := %fnCallback%(data))
					data := newData
				
				if this.responses.HasKey(data.ID)
					this.responses[data.ID] := data
			}
			else if (EventName == "Close")
			{
				this.Disconnect()
			}
			else if (EventName == "Error")
			{
				throw Exception("Websocket Error!")
			}
		}
		
		/*
			Disconnect from the page's debug interface, allowing the instance
			to be garbage collected.
			
			This method should always be called when you are finished with a
			page or else your script will leak memory.
		*/
		Disconnect()
		{
			if !this.Connected
				return
			
			this.Connected := False
			this.ws.Delete("Parent")
			this.ws.Disconnect()
			
			BoundKeepAlive := this.BoundKeepAlive
			SetTimer, %BoundKeepAlive%, Delete
			this.Delete("BoundKeepAlive")
		}
		
		class WebSocket
		{
			__New(WS_URL)
			{
				static wb
				
				; Create an IE instance
				Gui, +hWndhOld
				Gui, New, +hWndhWnd
				this.hWnd := hWnd
				Gui, Add, ActiveX, vWB, Shell.Explorer
				Gui, %hOld%: Default
				
				; Write an appropriate document
				WB.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible'"
				. "content='IE=edge'><body></body>")
				while (WB.ReadyState < 4)
					sleep, 50
				this.document := WB.document
				
				; Add our handlers to the JavaScript namespace
				this.document.parentWindow.ahk_savews := this._SaveWS.Bind(this)
				this.document.parentWindow.ahk_event := this._Event.Bind(this)
				this.document.parentWindow.ahk_ws_url := WS_URL
				
				; Add some JavaScript to the page to open a socket
				Script := this.document.createElement("script")
				Script.text := "ws = new WebSocket(ahk_ws_url);`n"
				. "ws.onopen = function(event){ ahk_event('Open', event); };`n"
				. "ws.onclose = function(event){ ahk_event('Close', event); };`n"
				. "ws.onerror = function(event){ ahk_event('Error', event); };`n"
				. "ws.onmessage = function(event){ ahk_event('Message', event); };"
				this.document.body.appendChild(Script)
			}
			
			; Called by the JS in response to WS events
			_Event(EventName, Event)
			{
				this["On" EventName](Event)
			}
			
			; Sends data through the WebSocket
			Send(Data)
			{
				this.document.parentWindow.ws.send(Data)
			}
			
			; Closes the WebSocket connection
			Close(Code:=1000, Reason:="")
			{
				this.document.parentWindow.ws.close(Code, Reason)
			}
			
			; Closes and deletes the WebSocket, removing
			; references so the class can be garbage collected
			Disconnect()
			{
				if this.hWnd
				{
					this.Close()
					Gui, % this.hWnd ": Destroy"
					this.hWnd := False
				}
			}
		}
	}
}

class LightJson
{
   static JS := LightJson.GetJS(), true := {}, false := {}, null := {}
   
   Parse(json, _rec := false) {
      if !_rec
         obj := this.Parse(this.JS.eval("(" . json . ")"), true)
      else if !IsObject(json)
         obj := json
      else if this.JS.Object.prototype.toString.call(json) == "[object Array]" {
         obj := []
         Loop % json.length
            obj.Push( this.Parse(json[A_Index - 1], true) )
      }
      else {
         obj := {}
         keys := this.JS.Object.keys(json)
         Loop % keys.length {
            k := keys[A_Index - 1]
            obj[k] := this.Parse(json[k], true)
         }
      }
      Return obj
   }
   
   Stringify(obj, indent := "") {
      if indent|1 {
         for k, v in ["true", "false", "null"]
            if (obj = this[v])
               Return v

         if IsObject( obj ) {
            isArray := true
            for key in obj {
               if IsObject(key)
                  throw Exception("Invalid key")
               if !( key = A_Index || isArray := false )
                  break
            }
            for k, v in obj
               str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : """" . k . """:" ) . this.Stringify(v, true)

            Return str = "" ? "{}" : isArray ? "[" . str . "]" : "{" . str . "}"
         }
         else if !(obj*1 = "" || RegExMatch(obj, "^-?0|\s"))
            Return obj
         
         for k, v in [["\", "\\"], [A_Tab, "\t"], ["""", "\"""], ["/", "\/"], ["`n", "\n"], ["`r", "\r"], [Chr(12), "\f"], [Chr(8), "\b"]]
            obj := StrReplace( obj, v[1], v[2] )

         Return """" obj """"
      }
      sObj := this.Stringify(obj, true)
      Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
   }
   
   GetJS() {
      static Doc, JS
      if !Doc {
         Doc := ComObjCreate("htmlfile")
         Doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         JS := Doc.parentWindow
         ( Doc.documentMode < 9 && JS.execScript() )
      }
      Return JS
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

122

Re: AHK: Класс для работы с JSON

Всё ок.
Видно у меня была старая версия LightJson, так-как с ней не работало, а с json от G33kDude работало.

123 (изменено: inseption86, 2023-05-11 17:28:38)

Re: AHK: Класс для работы с JSON

Добрый день! А возможно ли в имеющийся JSON добавить элементы?

Исходник:


{"tickets":[
      
  ]
}

Добавить:


{
      "ProfileID": "99200020",
      "Person":{
         "name": "АЛЕКСАНДР",
         "secondName": "АЛЕКСЕЕВИЧ",
         "lastName": "СМИРНОВ",
         "name_lat": "АЛЕКСАНДР",
         "secondName_lat": "ALEKSEEVICH",
         "lastName_lat": "SMIRNOV",
         "sex": "м",
         "dateOfBirth": "28.05.1986",
         "citizenship": "",
         "phone": "",
         "Passport": "",
         "TestHistory":[{
               "TestingData": "11.05.2023",
               "FirstName": "АЛЕКСАНДР",
               "SecondName": "АЛЕКСЕЕВИЧ",
               "LastName": "СМИРНОВ",
               "ProfileID": "",
               "Passport": "",
               "TestStatusDescription": "",
               "PlacementID": "",
               "UUID": "",
               "ExamName":""
            }]
        }
  }

чтобы по итогу получилось:


{"tickets":[{
      "ProfileID": "99200019",
      "Person":{
         "name": "АЛЕКСАНДР",
         "secondName": "АЛЕКСЕЕВИЧ",
         "lastName": "СМИРНОВ",
         "name_lat": "АЛЕКСАНДР",
         "secondName_lat": "ALEKSEEVICH",
         "lastName_lat": "SMIRNOV",
         "sex": "м",
         "dateOfBirth": "28.05.1986",
         "citizenship": "",
         "phone": "",
         "Passport": "",
         "TestHistory":[{
               "TestingData": "11.05.2023",
               "FirstName": "АЛЕКСАНДР",
               "SecondName": "АЛЕКСЕЕВИЧ",
               "LastName": "СМИРНОВ",
               "ProfileID": "",
               "Passport": "",
               "TestStatusDescription": "",
               "PlacementID": "",
               "UUID": "",
               "ExamName":""
            }]
        }
  },
{
      "ProfileID": "99200020",
      "Person":{
         "name": "АЛЕКСАНДР",
         "secondName": "АЛЕКСЕЕВИЧ",
         "lastName": "СМИРНОВ",
         "name_lat": "АЛЕКСАНДР",
         "secondName_lat": "ALEKSEEVICH",
         "lastName_lat": "SMIRNOV",
         "sex": "м",
         "dateOfBirth": "28.05.1986",
         "citizenship": "",
         "phone": "",
         "Passport": "",
         "TestHistory":[{
               "TestingData": "11.05.2023",
               "FirstName": "АЛЕКСАНДР",
               "SecondName": "АЛЕКСЕЕВИЧ",
               "LastName": "СМИРНОВ",
               "ProfileID": "",
               "Passport": "",
               "TestStatusDescription": "",
               "PlacementID": "",
               "UUID": "",
               "ExamName":""
            }]
        }
  }]
}

124

Re: AHK: Класс для работы с JSON

Можно только путём превращения json-строк в AHK-объекты, добавления одного объекта в другой, и снова превращения полученного нового AHK-объекта в json:

json1 =
(
{"tickets":[
      
  ]
}
)

json2 =
(
{
      "ProfileID": "99200020",
      "Person":{
         "name": "АЛЕКСАНДР",
         "secondName": "АЛЕКСЕЕВИЧ",
         "lastName": "СМИРНОВ",
         "name_lat": "АЛЕКСАНДР",
         "secondName_lat": "ALEKSEEVICH",
         "lastName_lat": "SMIRNOV",
         "sex": "м",
         "dateOfBirth": "28.05.1986",
         "citizenship": "",
         "phone": "",
         "Passport": "",
         "TestHistory":[{
               "TestingData": "11.05.2023",
               "FirstName": "АЛЕКСАНДР",
               "SecondName": "АЛЕКСЕЕВИЧ",
               "LastName": "СМИРНОВ",
               "ProfileID": "",
               "Passport": "",
               "TestStatusDescription": "",
               "PlacementID": "",
               "UUID": "",
               "ExamName":""
            }]
        }
  }
)

ahkObj1 := LightJson.Parse(json1)
ahkObj2 := LightJson.Parse(json2)

ahkObj1.tickets[1] := ahkObj2
compositeJson := LightJson.Stringify(ahkObj1, "    ")
MsgBox, % compositeJson

class LightJson
{
   static JS := LightJson.GetJS(), true := {}, false := {}, null := {}
   
   Parse(json, _rec := false) {
      if !_rec
         obj := this.Parse(this.JS.JSON.parse(json), true)
      else if !IsObject(json)
         obj := json
      else if this.JS.Object.prototype.toString.call(json) == "[object Array]" {
         obj := []
         Loop % json.length
            obj.Push( this.Parse(json[A_Index - 1], true) )
      }
      else {
         obj := {}
         keys := this.JS.Object.keys(json)
         Loop % keys.length {
            k := keys[A_Index - 1]
            obj[k] := this.Parse(json[k], true)
         }
      }
      Return obj
   }
   
   Stringify(obj, indent := "") {
      if indent|1 {
         for k, v in ["true", "false", "null"]
            if (obj = this[v])
               Return v

         if IsObject( obj ) {
            isArray := true
            for key in obj {
               if IsObject(key)
                  throw Exception("Invalid key")
               if !( key = A_Index || isArray := false )
                  break
            }
            for k, v in obj
               str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : this.Stringify(k, true) . ":" ) . this.Stringify(v, true)

            Return str = "" ? "{}" : isArray ? "[" . str . "]" : "{" . str . "}"
         }
         else if !(obj*1 = "" || RegExMatch(obj, "^-?0(?=[^.])|\s"))
            Return obj
         
         for k, v in [["\", "\\"], [A_Tab, "\t"], ["""", "\"""], ["/", "\/"], ["`n", "\n"], ["`r", "\r"], [Chr(12), "\f"], [Chr(8), "\b"]]
            obj := StrReplace( obj, v[1], v[2] )

         Return """" obj """"
      }
      sObj := this.Stringify(obj, true)
      Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
   }
   
   GetJS() {
      static Doc, JS
      if !Doc {
         Doc := ComObjCreate("htmlfile")
         Doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         JS := Doc.parentWindow
         ( Doc.documentMode < 9 && JS.execScript() )
      }
      Return JS
   }
}

Надо иметь в виду, что такой подход может изменить существующий json, потому что не все типы данных, которые поддерживает json, также поддерживает AHK, например при перекодировке json в AHK-объект вместо true и false появятся соответственно -1 и 0, так как AHK не поддерживает эти типы данных.
Я немного скорректировал класс LightJson.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

125

Re: AHK: Класс для работы с JSON

teadrinker

спасибо! А если json1 будет постоянен, а json2,3,4,5,6.... будет иметь такую же структуру, но с разными данными?

126

Re: AHK: Класс для работы с JSON

А в чём вопрос-то?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

127 (изменено: inseption86, 2024-02-05 14:56:24)

Re: AHK: Класс для работы с JSON

Добрый день, подскажите как можно вытащить этот элемент?



FileRead, var_text, % A_Desktop "\11.txt"
obj := JSON.Parse(var_text)

UUID := obj.TestHistory[1].UUID

MsgBox % obj.ExamCertificates.UUID[1].ID
Post's attachments

2024-02-05_135420.png 11.98 kb, file has never been downloaded. 

You don't have the permssions to download the attachments of this post.

128

Re: AHK: Класс для работы с JSON

Не стоит приводить текстовые данные, используя картинки.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

129

Re: AHK: Класс для работы с JSON

teadrinker



{
  "TestHistory": [
    {
      "UUID": "7d114b1e-aad9-4da4-b630-d9ef2e2dd056"
    }
  ],
  "ExamCertificates": {
    "7d114b1e-aad9-4da4-b630-d9ef2e2dd056": [
      {
        "ID": 3408889
      }
    ]
  }
}

130

Re: AHK: Класс для работы с JSON

obj := JSON.Parse(var_text)
MsgBox % obj.ExamCertificates[obj.TestHistory.1.UUID].1.ID
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

131

Re: AHK: Класс для работы с JSON

teadrinker
Спасибо!