Тема: AHK: Класс для работы с JSON
Создана аналогичная тема в Коллекции. Эта тема для возможной дискуссии, если в ней возникнет необходимость.
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Создана аналогичная тема в Коллекции. Эта тема для возможной дискуссии, если в ней возникнет необходимость.
Стоят упоминания моменты из другого обсуждения, начиная с этого, поднимающего эту тему?
В чем отличие от этого?
class JSON
{
Decode(jsonStr)
{
SC := ComObjCreate("ScriptControl")
SC.Language := "JScript"
ComObjError(false)
jsCode := "function arrangeForAhkTraversing(r){if(r instanceof Array){for(var a=0;a<r.length;++a)r[a]=arrangeForAhkTraversing(r[a]);return[" array ",r]}if(r instanceof Object){var n=[],e=[];for(var o in r)n.push(o),e.push(arrangeForAhkTraversing(r[o]));return[" object ",[n,e]]}return[typeof r,r]}"
SC.ExecuteStatement(jsCode "; obj=" jsonStr)
return this.convertJScriptObjToAhks( SC.Eval("arrangeForAhkTraversing(obj)") )
}
Encode(obj)
{
str := ""
array := true
for k in obj
{
if (k == A_Index)
continue
array := false
break
}
for a, b in obj
str .= (array ? "" : """" a """: ") . (IsObject(b) ? this.Encode(b) : """" b """") . ", "
str := RTrim(str, " ,")
return (array ? "[" str "]" : "{" str "}")
}
convertJScriptObjToAhks(jsObj)
{
if (jsObj[0] == "object")
{
obj := {}, keys := jsObj[1][0], values := jsObj[1][1]
loop % keys.length
obj[keys[A_INDEX-1]] := this.convertJScriptObjToAhks( values[A_INDEX-1] )
return obj
}
else if (jsObj[0] == "array")
{
array := []
loop % jsObj[1].length
array.insert(this.convertJScriptObjToAhks( jsObj[1][A_INDEX-1] ))
return array
}
else
return jsObj[1]
}
}
Стоят упоминания моменты из другого обсуждения, начиная с этого, поднимающего эту тему?
Не знаю, вам как кажется? У меня обычно проблем не возникает, если учитывать, что JSON не поддерживает 16-ричные числа.
В чем отличие от этого?
Глвное отличие в том, что здесь используется объект ScriptControl, а он есть только для 32-битных приложений, соответственно, код не универсальный.
Ну а вообще по-простому для парсинга JSON можно такой вариант использовать:
json =
(
{
"key": 6,
"array": [1, 2, 3],
"message": "Hello, World!"
}
)
oDoc := ComObjCreate("htmlfile")
oDoc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
try oDoc.parentWindow.eval("var message = JSON.parse('" . RegExReplace(json, "\R") . "').message;")
catch {
MsgBox, Invalid json!
Return
}
MsgBox, % oDoc.parentWindow.message
Можете скорректировать так, чтобы JSON.Stringify() возвращал строку преобразованную JSON.Parse() в исходное состояние, когда в ней встречаются "true", "false" или "null"?
str = {"read":true,"status":null}
oStr := JSON.Parse(str)
sStr := JSON.Stringify(oStr)
MsgBox % str "`n" sStr
Не думаю, что это возможно. Во-первых, -1 вместо true возвращает JS-объект по каким-то своим резонам после
oJSON := this.JS.("(" JsonString ")")
Во-вторых, даже если бы это было не так, в AHK true — эквивалент единицы:
MsgBox, % true
В-третьих, в AHK отсутствует аналог null, так что ему нечего показывать после
oStr := JSON.Parse(str)
Так что лучше просто не пользоваться подобными значениями.
Точно же, с "null" вообще двоякая ситуация получается в этой связи, если иметь ввиду именно пустое значение.
Благодарю за разъяснения.
А можно попросить Вас добавить "JSON.Stringify()", поддержку пользовательских символов для преодоления этого ограничения?
Например, объект формируется с заведомо некорректными строковыми значениями, в которых пользовательский символ предваряет начало сигнатуры строки, которую нужно изменить, если идущий за этим символом текст соответствует какому-то выбору. Что-то подобное этому:
str_origin = {"val_true":"_true","val_false":"_false","val_null":"_null","val_other":"_other"}
MsgBox % JSValues(str_origin, "_") "`n" JSValues(str_origin)
JSValues(str, sign := "") {
if (sign) {
reg_ex = "%sign%(true|false|null)"
Return RegExReplace(str, reg_ex, "$1")
} Return str
}
Не уверен, что это хороший вариант. А если в объекте попадётся реальная строка "_false"?
Для того, эта возможность и будет опциональной. Применять изменение строки нужно только в случае, если это будет требовать пользователь сообщая признак, по которому значение будет идентифицироваться. К тому же, признак можно комбинировать несколькими символами, сводя к нулю такую вероятность.
Дело в том, что отправляя JSON на сервер, его API может истолковывать сериализованную таким образом строку, как некорректную, ожидая в местах значений "-1", "0" и "", соответствующие им true, false и null и его не попросить, чтобы он сделал поправку на синтаксис AHK, который для него чуть своеобразней, чем требуется, хотя семантически это очень близкие значения(?строгая типизация?). Приходится дополнительно обрабатывать для этого отправляемые данные таким нехитрым способом, чтобы избежать ненужной реакции. По крайней мере, я, как разработчик могу на своей стороне предпринять дополнительные мероприятия чтобы быть уверенным, что сформированная из объекта строка будет описана верно, ведь это я её формирую и если бы мой инструмент был чуть более гибок, предоставляя "из коробки" средства для доп обработки, то это было бы только в плюс!
Ко всему, превращение объекта в строку и нужно скорее только для обратной связи, а раз так, то это нисколько не повлияет на логику, которая строится на входящих данных.
Ну, если опционально, можно попробовать. Как будет время, добавлю.
Можете скорректировать так, чтобы JSON.Stringify() возвращал строку преобразованную JSON.Parse() в исходное состояние, когда в ней встречаются "true", "false" или "null"?
Не думаю, что это возможно
Через ActiveScript можно:
js := new ActiveScript("JScript")
code =
(
var obj = JSON.parse('{"read":true,"status":null}');
var myJSON = JSON.stringify(obj);
)
js.Exec(code)
msgbox % js.myJSON
/*
* ActiveScript for AutoHotkey v1.1
*
* Provides an interface to Active Scripting languages like VBScript and JScript,
* without relying on Microsoft's ScriptControl, which is not available to 64-bit
* programs.
*
* License: Use, modify and redistribute without limitation, but at your own risk.
*/
class ActiveScript extends ActiveScript._base
{
__New(Language)
{
if this._script := ComObjCreate(Language, ActiveScript.IID)
this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
if !this._scriptParse
throw Exception("Invalid language", -1, Language)
this._site := new ActiveScriptSite(this)
this._SetScriptSite(this._site.ptr)
this._InitNew()
this._objects := {}
this.Error := ""
this._dsp := this._GetScriptDispatch() ; Must be done last.
try
if this.ScriptEngine() = "JScript"
this.SetJScript58()
}
SetJScript58()
{
static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
return false
VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
, "ptr", 0, "ptr", &var), ObjRelease(prop)
return hr >= 0
}
Eval(Code)
{
pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
this._ParseScriptText(Code, 0x20, pvar) ; SCRIPTTEXT_ISEXPRESSION := 0x20
return arr[0]
}
Exec(Code)
{
this._ParseScriptText(Code, 0x42, 0) ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
this._SetScriptState(2) ; SCRIPTSTATE_CONNECTED := 2
}
AddObject(Name, DispObj, AddMembers := false)
{
static a, supports_dispatch ; Test for built-in IDispatch support.
:= a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
this._objects[Name] := DispObj
this._AddNamedItem(Name, AddMembers ? 8 : 2) ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
}
_GetObjectUnk(Name)
{
return !IsObject(dsp := this._objects[Name]) ? dsp ; Pointer
: ComObjValue(dsp) ? ComObjValue(dsp) ; ComObject
: &dsp ; AutoHotkey object
}
class _base
{
__Call(Method, Params*)
{
if ObjHasKey(this, "_dsp")
try
return (this._dsp)[Method](Params*)
catch e
throw Exception(e.Message, -1, e.Extra)
}
__Get(Property, Params*)
{
if ObjHasKey(this, "_dsp")
try
return (this._dsp)[Property, Params*]
catch e
throw Exception(e.Message, -1, e.Extra)
}
__Set(Property, Params*)
{
if ObjHasKey(this, "_dsp")
{
Value := Params.Pop()
try
return (this._dsp)[Property, Params*] := Value
catch e
throw Exception(e.Message, -1, e.Extra)
}
}
}
_SetScriptSite(Site)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
if (hr < 0)
this._HRFail(hr, "IActiveScript::SetScriptSite")
}
_SetScriptState(State)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
if (hr < 0)
this._HRFail(hr, "IActiveScript::SetScriptState")
}
_AddNamedItem(Name, Flags)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
if (hr < 0)
this._HRFail(hr, "IActiveScript::AddNamedItem")
}
_GetScriptDispatch()
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
if (hr < 0)
this._HRFail(hr, "IActiveScript::GetScriptDispatch")
return ComObject(9, pdsp, 1)
}
_InitNew()
{
hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
if (hr < 0)
this._HRFail(hr, "IActiveScriptParse::InitNew")
}
_ParseScriptText(Code, Flags, pvarResult)
{
VarSetCapacity(excp, 8 * A_PtrSize, 0)
hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
, "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
, "uint", Flags, "ptr", pvarResult, "ptr", 0)
if (hr < 0)
this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
}
_HRFail(hr, what)
{
if e := this.Error
{
this.Error := ""
throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
. "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
. "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
. "`nLine text:`t`t" e.LineText, -3)
}
throw Exception(what " failed with code " this._HRFormat(hr), -2)
}
_HRFormat(hr)
{
return Format("0x{1:X}", hr & 0xFFFFFFFF)
}
_OnScriptError(err) ; IActiveScriptError err
{
VarSetCapacity(excp, 8 * A_PtrSize, 0)
DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
DllCall(fn, "ptr", &excp)
wcode := NumGet(excp, 0, "ushort")
hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
static Infos := "Source,Description,HelpFile"
Loop Parse, % Infos, `,
if pbstr := NumGet(excp, A_Index * A_PtrSize)
this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
}
__Delete()
{
if this._script
{
DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p) ; Close
ObjRelease(this._script)
}
if this._scriptParse
ObjRelease(this._scriptParse)
}
static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}
class ActiveScriptSite
{
__New(Script)
{
ObjSetCapacity(this, "_site", 3 * A_PtrSize)
NumPut(&Script
, NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
, NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
, this.ptr := ObjGetAddress(this, "_site"))))
}
_vftable(Name, PrmCounts, EIBase)
{
if p := ObjGetAddress(this, Name)
return p
ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
p := ObjGetAddress(this, Name)
Loop Parse, % PrmCounts
{
cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
NumPut(cb, p + (A_Index-1) * A_PtrSize)
}
return p
}
}
_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
Method := A_EventInfo & 0xFF
if A_EventInfo >= 0x100 ; IActiveScriptSiteWindow
{
if Method = 4 ; GetWindow
{
NumPut(0, a1+0) ; *phwnd := 0
return 0 ; S_OK
}
if Method = 5 ; EnableModeless
{
return 0 ; S_OK
}
this -= A_PtrSize ; Cast to IActiveScriptSite
}
;else: IActiveScriptSite
if Method = 1 ; QueryInterface
{
iid := _AS_GUIDToString(a1)
if (iid = "{00000000-0000-0000-C000-000000000046}" ; IUnknown
|| iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
{
NumPut(this, a2+0)
return 0 ; S_OK
}
if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
{
NumPut(this + A_PtrSize, a2+0)
return 0 ; S_OK
}
NumPut(0, a2+0)
return 0x80004002 ; E_NOINTERFACE
}
if Method = 5 ; GetItemInfo
{
a1 := StrGet(a1, "UTF-16")
, (a3 && NumPut(0, a3+0)) ; *ppiunkItem := NULL
, (a4 && NumPut(0, a4+0)) ; *ppti := NULL
if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
{
if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
ObjAddRef(unk), NumPut(unk, a3+0)
}
return 0 ; S_OK
}
if Method = 9 ; OnScriptError
return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
; AddRef and Release don't do anything because we want to avoid circular references.
; The site and IActiveScript are both released when the AHK script releases its last
; reference to the ActiveScript object.
; All of the other methods don't require implementations.
return 0x80004001 ; E_NOTIMPL
}
_AS_GUIDToString(pGUID)
{
VarSetCapacity(String, 38*2)
DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
return String
}
Здесь JSON.parse() возвращает не AHK-массив.
Ну и что из этого?
В смысле? Задача же была в AHK-массив перевести. Понятно, что JS умеет парсить JSON.
code =
(
var obj = JSON.parse('{"read":true,"status":null}');
var myJSON = JSON.stringify(obj);
)
oDoc := ComObjCreate("htmlfile")
oDoc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
try oDoc.parentWindow.eval(code)
catch {
MsgBox, Error
Return
}
MsgBox, % oDoc.parentWindow.myJSON
Так а почему мы не можем js объект перевести в ahk объект?
Ну попробуй этот объект перевести.
js := new JsRT.IE
code =
(
var obj = JSON.parse('{"read":true,"status":null}');
var keys = Object.keys(obj);
)
js.Exec(code)
loop % js.keys.length
{
key := (js.keys)[A_Index-1]
msgbox % key ":" (js.obj)[key]
}
/*
* JsRT for AutoHotkey v1.1
*
* Utilizes the JavaScript engine that comes with IE11.
*
* License: Use, modify and redistribute without limitation, but at your own risk.
*/
class JsRT extends ActiveScript._base
{
__New()
{
throw Exception("This class is abstract. Use JsRT.IE or JSRT.Edge instead.", -1)
}
class IE extends JsRT
{
__New()
{
if !this._hmod := DllCall("LoadLibrary", "str", "jscript9", "ptr")
throw Exception("Failed to load jscript9.dll", -1)
if DllCall("jscript9\JsCreateRuntime", "int", 0, "int", -1
, "ptr", 0, "ptr*", runtime) != 0
throw Exception("Failed to initialize JsRT", -1)
DllCall("jscript9\JsCreateContext", "ptr", runtime, "ptr", 0, "ptr*", context)
this._Initialize("jscript9", runtime, context)
}
}
class Edge extends JsRT
{
__New()
{
if !this._hmod := DllCall("LoadLibrary", "str", "chakra", "ptr")
throw Exception("Failed to load chakra.dll", -1)
if DllCall("chakra\JsCreateRuntime", "int", 0
, "ptr", 0, "ptr*", runtime) != 0
throw Exception("Failed to initialize JsRT", -1)
DllCall("chakra\JsCreateContext", "ptr", runtime, "ptr*", context)
this._Initialize("chakra", runtime, context)
}
ProjectWinRTNamespace(namespace)
{
return DllCall("chakra\JsProjectWinRTNamespace", "wstr", namespace)
}
}
_Initialize(dll, runtime, context)
{
this._dll := dll
this._runtime := runtime
this._context := context
DllCall(dll "\JsSetCurrentContext", "ptr", context)
DllCall(dll "\JsGetGlobalObject", "ptr*", globalObject)
this._dsp := this._JsToVt(globalObject)
}
__Delete()
{
this._dsp := ""
if dll := this._dll
{
DllCall(dll "\JsSetCurrentContext", "ptr", 0)
DllCall(dll "\JsDisposeRuntime", "ptr", this._runtime)
}
DllCall("FreeLibrary", "ptr", this._hmod)
}
_JsToVt(valref)
{
VarSetCapacity(variant, 24, 0)
DllCall(this._dll "\JsValueToVariant", "ptr", valref, "ptr", &variant)
ref := ComObject(0x400C, &variant), val := ref[], ref[] := 0
return val
}
_ToJs(val)
{
VarSetCapacity(variant, 24, 0)
ref := ComObject(0x400C, &variant) ; VT_BYREF|VT_VARIANT
ref[] := val
DllCall(this._dll "\JsVariantToValue", "ptr", &variant, "ptr*", valref)
ref[] := 0
return valref
}
_JsEval(code)
{
e := DllCall(this._dll "\JsRunScript", "wstr", code, "uptr", 0, "wstr", "source.js"
, "ptr*", result)
if e
{
if DllCall(this._dll "\JsGetAndClearException", "ptr*", excp) = 0
throw this._JsToVt(excp)
throw Exception("JsRT error", -2, format("0x{:X}", e))
}
return result
}
Exec(code)
{
this._JsEval(code)
}
Eval(code)
{
return this._JsToVt(this._JsEval(code))
}
AddObject(name, obj, addMembers := false)
{
if addMembers
throw Exception("AddMembers=true is not supported", -1)
this._dsp[name] := obj
}
}
/*
* ActiveScript for AutoHotkey v1.1
*
* Provides an interface to Active Scripting languages like VBScript and JScript,
* without relying on Microsoft's ScriptControl, which is not available to 64-bit
* programs.
*
* License: Use, modify and redistribute without limitation, but at your own risk.
*/
class ActiveScript extends ActiveScript._base
{
__New(Language)
{
if this._script := ComObjCreate(Language, ActiveScript.IID)
this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
if !this._scriptParse
throw Exception("Invalid language", -1, Language)
this._site := new ActiveScriptSite(this)
this._SetScriptSite(this._site.ptr)
this._InitNew()
this._objects := {}
this.Error := ""
this._dsp := this._GetScriptDispatch() ; Must be done last.
try
if this.ScriptEngine() = "JScript"
this.SetJScript58()
}
SetJScript58()
{
static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
return false
VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
, "ptr", 0, "ptr", &var), ObjRelease(prop)
return hr >= 0
}
Eval(Code)
{
pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
this._ParseScriptText(Code, 0x20, pvar) ; SCRIPTTEXT_ISEXPRESSION := 0x20
return arr[0]
}
Exec(Code)
{
this._ParseScriptText(Code, 0x42, 0) ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
this._SetScriptState(2) ; SCRIPTSTATE_CONNECTED := 2
}
AddObject(Name, DispObj, AddMembers := false)
{
static a, supports_dispatch ; Test for built-in IDispatch support.
:= a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
this._objects[Name] := DispObj
this._AddNamedItem(Name, AddMembers ? 8 : 2) ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
}
_GetObjectUnk(Name)
{
return !IsObject(dsp := this._objects[Name]) ? dsp ; Pointer
: ComObjValue(dsp) ? ComObjValue(dsp) ; ComObject
: &dsp ; AutoHotkey object
}
class _base
{
__Call(Method, Params*)
{
if ObjHasKey(this, "_dsp")
try
return (this._dsp)[Method](Params*)
catch e
throw Exception(e.Message, -1, e.Extra)
}
__Get(Property, Params*)
{
if ObjHasKey(this, "_dsp")
try
return (this._dsp)[Property, Params*]
catch e
throw Exception(e.Message, -1, e.Extra)
}
__Set(Property, Params*)
{
if ObjHasKey(this, "_dsp")
{
Value := Params.Pop()
try
return (this._dsp)[Property, Params*] := Value
catch e
throw Exception(e.Message, -1, e.Extra)
}
}
}
_SetScriptSite(Site)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
if (hr < 0)
this._HRFail(hr, "IActiveScript::SetScriptSite")
}
_SetScriptState(State)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
if (hr < 0)
this._HRFail(hr, "IActiveScript::SetScriptState")
}
_AddNamedItem(Name, Flags)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
if (hr < 0)
this._HRFail(hr, "IActiveScript::AddNamedItem")
}
_GetScriptDispatch()
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
if (hr < 0)
this._HRFail(hr, "IActiveScript::GetScriptDispatch")
return ComObject(9, pdsp, 1)
}
_InitNew()
{
hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
if (hr < 0)
this._HRFail(hr, "IActiveScriptParse::InitNew")
}
_ParseScriptText(Code, Flags, pvarResult)
{
VarSetCapacity(excp, 8 * A_PtrSize, 0)
hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
, "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
, "uint", Flags, "ptr", pvarResult, "ptr", 0)
if (hr < 0)
this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
}
_HRFail(hr, what)
{
if e := this.Error
{
this.Error := ""
throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
. "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
. "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
. "`nLine text:`t`t" e.LineText, -3)
}
throw Exception(what " failed with code " this._HRFormat(hr), -2)
}
_HRFormat(hr)
{
return Format("0x{1:X}", hr & 0xFFFFFFFF)
}
_OnScriptError(err) ; IActiveScriptError err
{
VarSetCapacity(excp, 8 * A_PtrSize, 0)
DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
DllCall(fn, "ptr", &excp)
wcode := NumGet(excp, 0, "ushort")
hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
static Infos := "Source,Description,HelpFile"
Loop Parse, % Infos, `,
if pbstr := NumGet(excp, A_Index * A_PtrSize)
this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
}
__Delete()
{
if this._script
{
DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p) ; Close
ObjRelease(this._script)
}
if this._scriptParse
ObjRelease(this._scriptParse)
}
static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}
class ActiveScriptSite
{
__New(Script)
{
ObjSetCapacity(this, "_site", 3 * A_PtrSize)
NumPut(&Script
, NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
, NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
, this.ptr := ObjGetAddress(this, "_site"))))
}
_vftable(Name, PrmCounts, EIBase)
{
if p := ObjGetAddress(this, Name)
return p
ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
p := ObjGetAddress(this, Name)
Loop Parse, % PrmCounts
{
cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
NumPut(cb, p + (A_Index-1) * A_PtrSize)
}
return p
}
}
_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
Method := A_EventInfo & 0xFF
if A_EventInfo >= 0x100 ; IActiveScriptSiteWindow
{
if Method = 4 ; GetWindow
{
NumPut(0, a1+0) ; *phwnd := 0
return 0 ; S_OK
}
if Method = 5 ; EnableModeless
{
return 0 ; S_OK
}
this -= A_PtrSize ; Cast to IActiveScriptSite
}
;else: IActiveScriptSite
if Method = 1 ; QueryInterface
{
iid := _AS_GUIDToString(a1)
if (iid = "{00000000-0000-0000-C000-000000000046}" ; IUnknown
|| iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
{
NumPut(this, a2+0)
return 0 ; S_OK
}
if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
{
NumPut(this + A_PtrSize, a2+0)
return 0 ; S_OK
}
NumPut(0, a2+0)
return 0x80004002 ; E_NOINTERFACE
}
if Method = 5 ; GetItemInfo
{
a1 := StrGet(a1, "UTF-16")
, (a3 && NumPut(0, a3+0)) ; *ppiunkItem := NULL
, (a4 && NumPut(0, a4+0)) ; *ppti := NULL
if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
{
if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
ObjAddRef(unk), NumPut(unk, a3+0)
}
return 0 ; S_OK
}
if Method = 9 ; OnScriptError
return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
; AddRef and Release don't do anything because we want to avoid circular references.
; The site and IActiveScript are both released when the AHK script releases its last
; reference to the ActiveScript object.
; All of the other methods don't require implementations.
return 0x80004001 ; E_NOTIMPL
}
_AS_GUIDToString(pGUID)
{
VarSetCapacity(String, 38*2)
DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
return String
}
Я вижу -1 и пустое значение.
Можно определять в js, что null = null, true = true и уже передавать в ahk с пометкой, что -1 - это не минус один, а true.
Кстати через htmlfile работает медленнее, чем через dllcall.
Можно определять в js, что null = null, true = true и уже передавать в ahk с пометкой, что -1 - это не минус один, а true.
Так а в чём отличие от моего JSON?
А у тебя разве определяется разница между true, единицей или минус единицей?
Если б определялось, тогда и говорить не о чем было. Я думал, ты привёл в пример ActiveScript, что через него это как-то удобнее делать.
Так я ж и пишу, что в ActiveScript мы можем проверять значения всех ключей и если значение true, то изменять его на znachenie_blah_blah_blah_true.
А в ahk уже проверять, если значение = znachenie_blah_blah_blah_true, то значит true.
А в чём отличие от моего JSON? Там тоже можно проверять.
Не знаю.
Я сделал выводы из твоей фразы -
Не думаю, что это возможно.
При преобразовании строки в AHK-объект, нет никаких проблем. От JSON.Parse() не ожидается больше предоставленного. Преобразованные данные всё равно потом участвуют дальше на стороне AHK и мы вольны воспринимать их как нам заблагорассудится. Но если объект преобразуется в строку и отправляется на сервер, то там работают правила неподвластного нам контекста и ожидают именно null в качестве null, или true в роли true. Просто строка - "true" не корректна, так как это стока, а минус единица - значение типа int, в то время как ожидается bool. Проблема только в этом.
Нужно только добавить JSON.Stringify() второй параметр со значением по умолчанию равным любому эквиваленту не истинности и всегда возвращать результат "обычного" преобразования если оно не менялось, иначе, не оборачивать в двойные кавычки каждое переданное "true", "false" и "null" при формировании JSON-строки, либо зареплейсить регуляркой финишный вариант, принимая значение второго параметра в качестве признака, вместе с которым будет формироваться каждый такой "_true", "_false", или "_null", как в примере номер девять.
Что оптимальней, не возьмусь сказать. Может есть ещё какой-либо хороший подход. Но для начинки это была бы весьма годная приспособа.
Спасибо за ваше участие!
Вообще сейчас true и false из AHK-объекта преобразуются в 1 и 0.
Наверное, от разрядности зависит. Я по некоторым причинам, вынужден x64 AHK пользовать. Но в целом, на обтекаемость, это не влияет.
На картинке обратная ситуация — перевод из JSON в AHK-объект.
Верно.
Прошу прощения, невнимательно прочитал.
Почему данный класс заменяет русские буквы на \u****?
Из:
["test", 123, "расишь текст"]
Получается:
["test",123,"\u00F0\u00E0\u00F1\u00E8\u00F8\u00FC \u00F2\u00E5\u00EA\u00F1\u00F2"]
Это очень мешает в дальнейшем.
Класс который я предоставлял в посте #3 такой беды не делает, наверное именно по этому я его выбрал.. Но у одного человека он вызвал ошибку из-за "ScriptControl".
Вот специально для вас вариант через htmlfile, не конвертирует кириллицу:
obj := {ключ: [{ключ: "значение"}, {ключ: "значение"}] }
MsgBox, % sJSON := JSON.Stringify(obj)
obj := JSON.Parse(sJSON)
MsgBox, % obj["ключ", 1, "ключ"]
class JSON
{
static JS := JSON._GetJScripObject()
Parse(JsonString) {
try oJSON := this.JS.("(" JsonString ")")
catch {
MsgBox, Wrong JsonString!
Return
}
Return this._CreateObject(oJSON)
}
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]
}
Stringify(obj) {
sObj := this._ObjToString(obj)
Return this.JS.("JSON.stringify(" . sObj . ")")
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScripObject() {
static doc
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := ObjBindMethod(doc.parentWindow, "eval")
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
}
}
Обнаружите баги — пишите.
Новая версия, добавлена поддержка null, true, false, добавлены методы GetKey(), SetKey(), RemoveKey(). Если кто обнаружит баги — пишите.
sJson = { ключ: [{ключ: "значение"}, {ключ: true}] }
obj := JSON.Parse(sJSON) ; обычный способ, получаем ahk-объект
MsgBox, % obj["ключ", 2, "ключ"] ; -1 вместо true
jsObj := JSON.Parse(sJSON, true) ; второй параметр true — предполагается, что на выходе js-объект
jsObj["ключ"][0]["ключ"] := "value" ; заменяем "значение" на "value"
MsgBox, % sJson := JSON.Stringify(jsObj, true) ; второй параметр true — предполагается, что парсится js-объект, теперь "ключ: true", как положено
MsgBox, % sJson := JSON.RemoveKey(sJson, "ключ") ; удаляем ключ "ключ", остаётся {}
MsgBox, % sJson := JSON.SetKey(sJSON, "key1", "'Привет'")
MsgBox, % sJson := JSON.SetKey(sJson, "key2", "true")
MsgBox, % JSON.GetKey(sJson, "key2")
MsgBox, % sJson := JSON.SetKey(sJson, "key3", "{'key1':'value', 'key2':['one', null]}")
MsgBox, % sJson := JSON.SetKey(sJson, "key3.key2[2]", "false")
MsgBox, % JSON.GetKey(sJson, "key3")
MsgBox, % JSON.GetKey(sJson, "key3.key2[2]")
MsgBox, % sJson := JSON.RemoveKey(sJson, "key3.key2[2]") ; удаляем третий (null-based) элемент массива в ключе key3.key2
class JSON
{
static JS := JSON._GetJScripObject()
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false) {
if js
Return this.JS.JSON.stringify(obj)
else {
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ")")
}
}
GetKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
try res := this.JS.eval("JSON.stringify((" . sJson . ")." . key . ")")
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
Return res
}
SetKey(sJson, key, value) {
if !(this.VerifyJson(sJson) && this.VerifyJson(value))
Return
res := this.JS.eval("var obj = (" . sJson . "); obj." . key . "=" . value . "; JSON.stringify(obj)")
this.JS.eval("obj = ''")
Return res
}
RemoveKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
if RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); obj." . match1 . ".splice(" . match2 . ", 1); JSON.stringify(obj)")
else
res := this.JS.eval("var obj = (" . sJson . "); delete obj." . key . "; JSON.stringify(obj)")
this.JS.eval("obj = ''")
Return res
}
VerifyJson(sJson) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
MsgBox, Wrong JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScripObject() {
static doc
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
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.eval(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
}
}
Некоторые исправления, добавлен метод Enum().
sJson = [null, false, {"key1": 5, "key2": true, "key3": "string"}, {"key1": 0, "key2": false, "key3": "AHK"}]
for k, v in JSON.Enum(sJson)
MsgBox, % "key: " k "`nvalue: " v
for k, v in JSON.Enum(sJson)
for k, v in JSON.Enum(v)
MsgBox, % "key: " k "`nvalue: " v
for k, v in JSON.Enum(sJson, "[3]")
MsgBox, % "key: " k "`nvalue: " v
class JSON
{
static JS := JSON._GetJScriptObject()
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false) {
if js
Return this.JS.JSON.stringify(obj)
else {
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ")")
}
}
GetKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
Return res
}
SetKey(sJson, key, value) {
if !(this.VerifyJson(sJson) && this.VerifyJson(value))
Return
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
RemoveKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
sign := SubStr(key, 1, 1) = "[" ? "" : "."
if !RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
else
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
Enum(sJson, key := "") {
if !this.VerifyJson(sJson)
Return
conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
try jsObj := this.JS.eval("(" sJson ")" . conc)
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
res := jsObj.IsArray()
if (res = "")
Return
obj := {}
if (res = -1) {
Loop % jsObj.length
obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
}
else if (res = 0) {
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
}
Return obj
}
VerifyJson(sJson) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
MsgBox, Wrong JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScriptObject() {
static doc
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
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.eval(JScript)
}
_CreateObject(jsObj) {
res := jsObj.IsArray()
if (res = "")
Return jsObj
else if (res = -1) {
obj := []
Loop % jsObj.length
obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
}
Return obj
}
}
Вот специально для вас вариант через htmlfile, не конвертирует кириллицу:
obj := {ключ: [{ключ: "значение"}, {ключ: "значение"}] } MsgBox, % sJSON := JSON.Stringify(obj) obj := JSON.Parse(sJSON) MsgBox, % obj["ключ", 1, "ключ"] class JSON { static JS := JSON._GetJScripObject() Parse(JsonString) { try oJSON := this.JS.("(" JsonString ")") catch { MsgBox, Wrong JsonString! Return } Return this._CreateObject(oJSON) } 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] } Stringify(obj) { sObj := this._ObjToString(obj) Return this.JS.("JSON.stringify(" . sObj . ")") } _ObjToString(obj) { 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._ObjToString(v) Return isArray ? "[" str "]" : "{" str "}" } else if !(obj*1 = "" || RegExMatch(obj, "\s")) Return obj else Return """" obj """" } _GetJScripObject() { static doc doc := ComObjCreate("htmlfile") doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">") JS := ObjBindMethod(doc.parentWindow, "eval") 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 } }
Обнаружите баги — пишите.
Начались ошибки у людей:
-------------------------—
Script.exe
-------------------------—
Error: 0x80020006 - Неизвестное имя.
Specifically: eval
Line#
---> 6245: JS.("delete ActiveXObject; delete GetObject;")
Continue running the script?
-------------------------—
Да Нет
---------------------------
Здесь ошибки не должно быть, по идее, во всяком случае первый раз слышу о такой, выглядит, как какая-то системная проблема. Код для теста:
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
MsgBox, % JS.eval("({""key"":""value""})").key ; MsgBox должно показать value
Если не сработает, значит что-то не так в системе.
Может, установлен IE ниже, чем 9?
teadrinker, а почему не использовать ActiveScript?
Если делать для массового пользования, то полагаться на настройки и версиии IE не совсем надёжно.
Пока-то не было проблем. Попробую с ActiveScript.
Phoenixxx_Czar, проверьте ещё:
IE —> Свойства браузера —> Безопасность:
Кнопка Другой...
Сценарии —> Активные сценарии —> Включить
Вспомнил, для IE ниже 9 версии надо инициализировать JavaScript интерпретатор:
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=6"">")
msgbox % Doc.documentMode
JS := doc.parentWindow
JS.execScript("eval()")
MsgBox, % JS.eval("({""key"":""value""})").key ; MsgBox должно показать value
Первая версия ошибку выдала.
Вторая заработала.
1. 5.00...
2. value
И с классом который вы давали в первый раз тоже стало работать, без изменения кода (магия какая-то).
teadrinker, пока не пригодилось.
а почему не использовать ActiveScript?
Поэтому:
ahkObj := { ключ: [{ключ: "значение"}, {ключ: true}] }
MsgBox, % JSON.Stringify(ahkObj)
/*
* ActiveScript for AutoHotkey v1.1
*
* Provides an interface to Active Scripting languages like VBScript and JScript,
* without relying on Microsoft's ScriptControl, which is not available to 64-bit
* programs.
*
* License: Use, modify and redistribute without limitation, but at your own risk.
*/
class ActiveScript extends ActiveScript._base
{
__New(Language)
{
if this._script := ComObjCreate(Language, ActiveScript.IID)
this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
if !this._scriptParse
throw Exception("Invalid language", -1, Language)
this._site := new ActiveScriptSite(this)
this._SetScriptSite(this._site.ptr)
this._InitNew()
this._objects := {}
this.Error := ""
this._dsp := this._GetScriptDispatch() ; Must be done last.
try
if this.ScriptEngine() = "JScript"
this.SetJScript58()
}
SetJScript58()
{
static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
return false
VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
, "ptr", 0, "ptr", &var), ObjRelease(prop)
return hr >= 0
}
Eval(Code)
{
pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
this._ParseScriptText(Code, 0x20, pvar) ; SCRIPTTEXT_ISEXPRESSION := 0x20
return arr[0]
}
Exec(Code)
{
this._ParseScriptText(Code, 0x42, 0) ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
this._SetScriptState(2) ; SCRIPTSTATE_CONNECTED := 2
}
AddObject(Name, DispObj, AddMembers := false)
{
static a, supports_dispatch ; Test for built-in IDispatch support.
:= a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
this._objects[Name] := DispObj
this._AddNamedItem(Name, AddMembers ? 8 : 2) ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
}
_GetObjectUnk(Name)
{
return !IsObject(dsp := this._objects[Name]) ? dsp ; Pointer
: ComObjValue(dsp) ? ComObjValue(dsp) ; ComObject
: &dsp ; AutoHotkey object
}
class _base
{
__Call(Method, Params*)
{
if ObjHasKey(this, "_dsp")
try
return (this._dsp)[Method](Params*)
catch e
throw Exception(e.Message, -1, e.Extra)
}
__Get(Property, Params*)
{
if ObjHasKey(this, "_dsp")
try
return (this._dsp)[Property, Params*]
catch e
throw Exception(e.Message, -1, e.Extra)
}
__Set(Property, Params*)
{
if ObjHasKey(this, "_dsp")
{
Value := Params.Pop()
try
return (this._dsp)[Property, Params*] := Value
catch e
throw Exception(e.Message, -1, e.Extra)
}
}
}
_SetScriptSite(Site)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
if (hr < 0)
this._HRFail(hr, "IActiveScript::SetScriptSite")
}
_SetScriptState(State)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
if (hr < 0)
this._HRFail(hr, "IActiveScript::SetScriptState")
}
_AddNamedItem(Name, Flags)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
if (hr < 0)
this._HRFail(hr, "IActiveScript::AddNamedItem")
}
_GetScriptDispatch()
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
if (hr < 0)
this._HRFail(hr, "IActiveScript::GetScriptDispatch")
return ComObject(9, pdsp, 1)
}
_InitNew()
{
hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
if (hr < 0)
this._HRFail(hr, "IActiveScriptParse::InitNew")
}
_ParseScriptText(Code, Flags, pvarResult)
{
VarSetCapacity(excp, 8 * A_PtrSize, 0)
hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
, "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
, "uint", Flags, "ptr", pvarResult, "ptr", 0)
if (hr < 0)
this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
}
_HRFail(hr, what)
{
if e := this.Error
{
this.Error := ""
throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
. "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
. "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
. "`nLine text:`t`t" e.LineText, -3)
}
throw Exception(what " failed with code " this._HRFormat(hr), -2)
}
_HRFormat(hr)
{
return Format("0x{1:X}", hr & 0xFFFFFFFF)
}
_OnScriptError(err) ; IActiveScriptError err
{
VarSetCapacity(excp, 8 * A_PtrSize, 0)
DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
DllCall(fn, "ptr", &excp)
wcode := NumGet(excp, 0, "ushort")
hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
static Infos := "Source,Description,HelpFile"
Loop Parse, % Infos, `,
if pbstr := NumGet(excp, A_Index * A_PtrSize)
this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
}
__Delete()
{
if this._script
{
DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p) ; Close
ObjRelease(this._script)
}
if this._scriptParse
ObjRelease(this._scriptParse)
}
static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}
class ActiveScriptSite
{
__New(Script)
{
ObjSetCapacity(this, "_site", 3 * A_PtrSize)
NumPut(&Script
, NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
, NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
, this.ptr := ObjGetAddress(this, "_site"))))
}
_vftable(Name, PrmCounts, EIBase)
{
if p := ObjGetAddress(this, Name)
return p
ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
p := ObjGetAddress(this, Name)
Loop Parse, % PrmCounts
{
cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
NumPut(cb, p + (A_Index-1) * A_PtrSize)
}
return p
}
}
_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
Method := A_EventInfo & 0xFF
if A_EventInfo >= 0x100 ; IActiveScriptSiteWindow
{
if Method = 4 ; GetWindow
{
NumPut(0, a1+0) ; *phwnd := 0
return 0 ; S_OK
}
if Method = 5 ; EnableModeless
{
return 0 ; S_OK
}
this -= A_PtrSize ; Cast to IActiveScriptSite
}
;else: IActiveScriptSite
if Method = 1 ; QueryInterface
{
iid := _AS_GUIDToString(a1)
if (iid = "{00000000-0000-0000-C000-000000000046}" ; IUnknown
|| iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
{
NumPut(this, a2+0)
return 0 ; S_OK
}
if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
{
NumPut(this + A_PtrSize, a2+0)
return 0 ; S_OK
}
NumPut(0, a2+0)
return 0x80004002 ; E_NOINTERFACE
}
if Method = 5 ; GetItemInfo
{
a1 := StrGet(a1, "UTF-16")
, (a3 && NumPut(0, a3+0)) ; *ppiunkItem := NULL
, (a4 && NumPut(0, a4+0)) ; *ppti := NULL
if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
{
if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
ObjAddRef(unk), NumPut(unk, a3+0)
}
return 0 ; S_OK
}
if Method = 9 ; OnScriptError
return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
; AddRef and Release don't do anything because we want to avoid circular references.
; The site and IActiveScript are both released when the AHK script releases its last
; reference to the ActiveScript object.
; All of the other methods don't require implementations.
return 0x80004001 ; E_NOTIMPL
}
_AS_GUIDToString(pGUID)
{
VarSetCapacity(String, 38*2)
DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
return String
}
class JSON
{
static JS := JSON._GetJScriptObject()
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false) {
if js
Return this.JS.JSON.stringify(obj)
else {
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ")")
}
}
GetKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
Return res
}
SetKey(sJson, key, value) {
if !(this.VerifyJson(sJson) && this.VerifyJson(value))
Return
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
RemoveKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
sign := SubStr(key, 1, 1) = "[" ? "" : "."
if !RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
else
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
Enum(sJson, key := "") {
if !this.VerifyJson(sJson)
Return
conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
try jsObj := this.JS.eval("(" sJson ")" . conc)
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
res := jsObj.IsArray()
if (res = "")
Return
obj := {}
if (res = -1) {
Loop % jsObj.length
obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
}
else if (res = 0) {
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
}
Return obj
}
VerifyJson(sJson) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
MsgBox, Wrong JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScriptObject() {
JS := new ActiveScript("JScript")
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.eval(JScript)
}
_CreateObject(jsObj) {
res := jsObj.IsArray()
if (res = "")
Return jsObj
else if (res = -1) {
obj := []
Loop % jsObj.length
obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
}
Return obj
}
}
Сравни:
ahkObj := { ключ: [{ключ: "значение"}, {ключ: true}] }
MsgBox, % JSON.Stringify(ahkObj)
class JSON
{
static JS := JSON._GetJScriptObject()
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false) {
if js
Return this.JS.JSON.stringify(obj)
else {
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ")")
}
}
GetKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
Return res
}
SetKey(sJson, key, value) {
if !(this.VerifyJson(sJson) && this.VerifyJson(value))
Return
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
RemoveKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
sign := SubStr(key, 1, 1) = "[" ? "" : "."
if !RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
else
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
Enum(sJson, key := "") {
if !this.VerifyJson(sJson)
Return
conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
try jsObj := this.JS.eval("(" sJson ")" . conc)
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
res := jsObj.IsArray()
if (res = "")
Return
obj := {}
if (res = -1) {
Loop % jsObj.length
obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
}
else if (res = 0) {
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
}
Return obj
}
VerifyJson(sJson) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
MsgBox, Wrong JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScriptObject() {
static doc
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
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.eval(JScript)
}
_CreateObject(jsObj) {
res := jsObj.IsArray()
if (res = "")
Return jsObj
else if (res = -1) {
obj := []
Loop % jsObj.length
obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
}
Return obj
}
}
Да, JScript конвертит.
Надо через JSRT.
new JsRT.IE
Так нормально, но
Utilizes the JavaScript engine that comes with IE11.
Надо ещё проверить, независим ли он от настроек IE.
Нет поддержки int64:
val = {"a": 17849713558010739}
MsgBox, % JSON.Parse(val).a
А должно 17849713558010739.
Так javascript выдаёт, и на JsRT так же.
Ну да. Поэтому я и не написал, что баг, а то, что нет поддержки.
В библиотеке у Coco есть.
В консоли Хрома так же:
Ну вряд ли кому понадобится туда 64-битные числа пихать.
Апи инстаграма возвращает.
http://forum.script-coding.com/viewtopi … 48#p135348
У меня выдаёт
Я о том же вчера писал в соседней теме. Есть ли способ получать числа без изменений?
Malcev
Вот-вот)).
Да, использовать парсер от Coco:
https://github.com/cocobelgica/AutoHotkey-JSON
Оказалось, что если перед парсингом пройтись регулярным выражением и заключить нужные числа в кавычки
"pk": "\d+"
Тогда числа не меняются.
использовать парсер от Coco
Так и не понял, куда какие вписывать объекты, ключи и т.д., для строки Инстаграма.
Там свои тонкости и баги - если интересно, создавайте отдельную тему.
Оптимальный, наверное, вариант - взять оттуда алгоритм преобразования числовых значений в строчные и вставить в парсер от teadrinker.
Я наверно числа буду закавычивать перед обработкой. Но вот на парсер от teadrinker хотелось бы перейти. Как в нём для строки Инстаграма выводить следующие данные из "caption" и "comments"?
"caption" [text, created_at, user_id, username]
"comments" [text, created_at, pk, user_id, username]
sJson = {"comment_likes_enabled": true, "comments": [{"pk": 17849713558010739, "user_id": 198212294, "text": "Тире ставится, когда есть пробел", "type": 0, "created_at": 1440283999, "created_at_utc": 1440283999, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": 198212294, "username": "artijado", "full_name": "Igor Malinin", "is_private": false, "profile_pic_url": "https://scontent-arn2-1.cdninstagram.com/vp/cc261c5894efaa68c8e5e7d40c02f103/5DE7C76F/t51.2885-19/s150x150/60960188_330080244351723_7306559494501171200_n.jpg?_nc_ht=scontent-arn2-1.cdninstagram.com", "profile_pic_id": "2053930082462014902_198212294", "is_verified": false, "latest_reel_media": 1565204355}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 0, "inline_composer_display_condition": "never"}], "comment_count": 1, "caption": {"pk": 17852726821010739, "user_id": 1545290738, "text": "Уточнила написание числовых интервалов в \"Справочнике издателя и автора\" Мильчина и Чельцовой: между цифрами ставится широченное тире (а я ставил минус из скромности, он всё же не так режет глаз своей шириной). Забавно, что при этом на обложке самого справочника красуется дефис.\n\n#книжныйдизайн #emdash #мильчин #издал #artlebedev", "type": 1, "created_at": 1447723802, "created_at_utc": 1447723802, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": 1545290738, "username": "addaald", "full_name": "Adda Ald", "is_private": false, "profile_pic_url": "https://scontent-arn2-1.cdninstagram.com/vp/5244ed9c2b65d6d8e016149616e58e77/5DC8DF29/t51.2885-19/s150x150/62204076_2392406174113939_623129493279604736_n.jpg?_nc_ht=scontent-arn2-1.cdninstagram.com", "profile_pic_id": "2064098053023530828_1545290738", "is_verified": false}, "did_report_as_spam": false, "share_enabled": false}, "caption_is_edited": true, "has_more_comments": false, "has_more_headload_comments": false, "media_header_display": "none", "display_realtime_typing_indicator": true, "preview_comments": [], "can_view_more_preview_comments": false, "status": "ok"}
; одиночный ключ, сохраняет true/false/null
MsgBox, % JSON.GetKey(sJson, "comments[0].text")
; одиночный ключ из ahk-объекта, не сохраняет true/false/null
ahkObj := JSON.Parse(sJson)
MsgBox, % ahkObj.comments[1].text
; парсинг JSON, такой парсинг нужно использовать, чтобы сохранить true/false/null
for k, v in JSON.Enum(sJson, "comments[0]")
MsgBox, % "key:" . A_Tab . k "`nvalue:" . A_Tab . v
MsgBox, 48,, Парсинг comments[0] завершён!
for k, v in JSON.Enum(sJson, "caption")
MsgBox, % "key:" . A_Tab . k "`nvalue:" . A_Tab . v
MsgBox, 48,, Парсинг caption завершён!
class JSON
{
static JS := JSON._GetJScriptObject()
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false) {
if js
Return this.JS.JSON.stringify(obj)
else {
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ")")
}
}
GetKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
Return res
}
SetKey(sJson, key, value) {
if !(this.VerifyJson(sJson) && this.VerifyJson(value))
Return
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
RemoveKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
sign := SubStr(key, 1, 1) = "[" ? "" : "."
if !RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
else
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
Enum(sJson, key := "") {
if !this.VerifyJson(sJson)
Return
conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
try jsObj := this.JS.eval("(" sJson ")" . conc)
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
res := jsObj.IsArray()
if (res = "")
Return
obj := {}
if (res = -1) {
Loop % jsObj.length
obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
}
else if (res = 0) {
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
}
Return obj
}
VerifyJson(sJson) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
MsgBox, Wrong JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScriptObject() {
static doc
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
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.eval(JScript)
}
_CreateObject(jsObj) {
res := jsObj.IsArray()
if (res = "")
Return jsObj
else if (res = -1) {
obj := []
Loop % jsObj.length
obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
}
Return obj
}
}
Парсинг ahk-объекта не приводил, надеюсь, с ним понятно.
Спасибо, мощно! Есть ли настройка, чтобы некоторые ключи брались без кавычек?
; одиночный ключ, сохраняет true/false/null
;MsgBox, % JSON.GetKey(sJson, "comments[0].text")
text := JSON.GetKey(sJson, "comments[0].text")
created_at := JSON.GetKey(sJson, "comments[0].created_at")
cmt_id := JSON.GetKey(sJson, "comments[0].pk")
comment_like_count := JSON.GetKey(sJson, "comments[0].comment_like_count")
user_id := JSON.GetKey(sJson, "comments[0].user_id")
username := JSON.GetKey(sJson, "comments[0].user.username")
full_name := JSON.GetKey(sJson, "comments[0].user.full_name")
profile_pic_id := JSON.GetKey(sJson, "comments[0].user.profile_pic_id")
profile_pic_url := JSON.GetKey(sJson, "comments[0].user.profile_pic_url")
list .= created_at " | " text " | " cmt_id " | " full_name " | " username " | " user_id " | " profile_pic_id " | " comment_like_count "`n"
msgbox % list
Вроде так — но тогда числа начинают дробиться
ahkObj := JSON.Parse(sJson)
text := ahkObj.comments[1].text
created_at := ahkObj.comments[1].created_at
cmt_id := ahkObj.comments[1].pk
comment_like_count := ahkObj.comments[1].comment_like_count
user_id := ahkObj.comments[1].user_id
username := ahkObj.comments[1].user.username
full_name := ahkObj.comments[1].user.full_name
profile_pic_id := ahkObj.comments[1].user.profile_pic_id
profile_pic_url := ahkObj.comments[1].user.profile_pic_url
При использовании GetKey() и Enum() ключи выводятся как есть в тексте, со всеми кавычками. Без кавычек можно взять из ahk-объекта, но с потерей true/false/null.
Подобные задачи можно решать циклом:
for k, v in ["text", "created_at", "pk", "comment_like_count", "user_id"]
%v% := JSON.GetKey(sJson, "comments[0]." . v)
for k, v in ["user_name", "full_name", "profile_pic_id", "profile_pic_url"]
%v% := JSON.GetKey(sJson, "comments[0].user." . v)
list .= created_at " | " text " | " cmt_id " | " full_name " | " username " | " user_id " | " profile_pic_id " | " comment_like_count
msgbox % list
teadrinker, это выводилось из "comments" и "comments.user". А как из "caption" вывести?
Смотрите структуру JSON, определяйте путь к нужным ключам. Это удобно делать с помощью каких-либо плагинов к браузерам, например:
Видим, что в comments все ключи находятся в списке (фигурные скобки), который, в свою очередь, находится в массиве (квадратные скобки), и является его первым ключом, поэтому путь через [0].
В caption другое устройство:
Предоставляю вам возможность самостоятельно догадаться, как правильно указать путь.
Готово, спасибо за задачку) —
for k, v in ["text", "created_at", "user_id"]
%v% := JSON.GetKey(sJson, "caption." . v)
for k, v in ["username", "full_name", "profile_pic_id"]
%v% := JSON.GetKey(sJson, "caption.user." . v)
list .= created_at " | " text " | " full_name " | " username " | " user_id " | " profile_pic_id
msgbox % list
teadrinker, а с чем связано что "username" повторяется, хотя они отличаются в JSON-строке? —
del
А там разве есть такой ключ — user_name, который у вас во втором цикле?
Точно!)) Видимо, с 66-го комментария закралось)).
Возник вопрос, как собирать все комменты в общую переменную? Вроде нужен цикл, но как его применить?
sJson =
(
{"comment_likes_enabled": true, "comments": [{"pk": "17940178414272410", "user_id": "10843926123", "text": "А я люблю многоточие. Помнишь, у Набокова:\"Следы на цыпочках ушедших слов\"?", "type": 0, "created_at": 1553931298, "created_at_utc": 1553931298, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "10843926123", "username": "kachalova_lubov", "full_name": "Любовь Качалова. Коуч", "is_private": false, "profile_pic_url": "https", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 3, "inline_composer_display_condition": "never"}, {"pk": "17988326917202163", "user_id": "1553194130", "text": "@kachalova_lubov именно «ушедших». В публицистике должно быть больше конкретики. Работая редактором, часто истребляла многоточия.", "type": 2, "created_at": 1553932059, "created_at_utc": 1553932059, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "inline_composer_display_condition": "never"}, {"pk": "18049568809024649", "user_id": "19127031", "text": "Многоточия да! Я в переписке всегда спрашиваю, что вы под этим .... имеете ввиду) больше мне люди так не пишут))", "type": 0, "created_at": 1553937465, "created_at_utc": 1553937465, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "19127031", "username": "tadddddyy", "full_name": "Tatiana Druzhnyaeva", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1879693248769800373_19127031", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 1, "inline_composer_display_condition": "never"}], "comment_count": 6, "caption": {"pk": "17884550440314611", "user_id": "1553194130", "text": "Нет ничего мерзее многоточий", "type": 1, "created_at": 1553930114, "created_at_utc": 1553930114, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false}, "did_report_as_spam": false, "share_enabled": false, "has_translation": true}, "caption_is_edited": true, "has_more_comments": false, "has_more_headload_comments": false, "media_header_display": "none", "display_realtime_typing_indicator": true, "preview_comments": [], "can_view_more_preview_comments": false, "status": "ok"}
)
for k, v in ["text", "created_at", "pk", "comment_like_count", "user_id"]
%v% := JSON.GetKey(sJson, "comments[0]." . v)
for k, v in ["username", "full_name", "profile_pic_id", "profile_pic_url"]
%v% := JSON.GetKey(sJson, "comments[0].user." . v)
comments .= created_at " | " text " | " pk " | " full_name " | " username " | " user_id " | " profile_pic_id " | " comment_like_count " | " profile_pic_url
all .= comments
msgbox % all
class JSON
{
static JS := JSON._GetJScriptObject()
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false) {
if js
Return this.JS.JSON.stringify(obj)
else {
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ")")
}
}
GetKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
Return res
}
SetKey(sJson, key, value) {
if !(this.VerifyJson(sJson) && this.VerifyJson(value))
Return
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
RemoveKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
sign := SubStr(key, 1, 1) = "[" ? "" : "."
if !RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
else
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
Enum(sJson, key := "") {
if !this.VerifyJson(sJson)
Return
conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
try jsObj := this.JS.eval("(" sJson ")" . conc)
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
res := jsObj.IsArray()
if (res = "")
Return
obj := {}
if (res = -1) {
Loop % jsObj.length
obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
}
else if (res = 0) {
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
}
Return obj
}
VerifyJson(sJson) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
MsgBox, Wrong JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScriptObject() {
static doc
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
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.eval(JScript)
}
_CreateObject(jsObj) {
res := jsObj.IsArray()
if (res = "")
Return jsObj
else if (res = -1) {
obj := []
Loop % jsObj.length
obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
}
Return obj
}
}
Не понял, в чём вопрос. Как удобнее, так и собирайте.
В смысле, собрать по принципу, —
comments .= ...
когда в результате все они будут в виде списка. Почему-то после первого коммента, остальные пропускаются.
Я так и не понял, как должна выглядеть строка на выходе.
teadrinker
В предыдущем скрипте, которым я пользовался для JSON обработки, все комментарии выводились за один раз (см. код ниже), а в Вашем выводится только первый коммент (см. код в 73-м сообщении). Поэтому хотелось понять, как их выводить все разом.
ResponseText =
(
{"comment_likes_enabled": true, "comments": [{"pk": "17940178414272410", "user_id": "10843926123", "text": "А я люблю многоточие. Помнишь, у Набокова:\"Следы на цыпочках ушедших слов\"?", "type": 0, "created_at": 1553931298, "created_at_utc": 1553931298, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "10843926123", "username": "kachalova_lubov", "full_name": "Любовь Качалова. Коуч", "is_private": false, "profile_pic_url": "https", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 3, "inline_composer_display_condition": "never"}, {"pk": "17988326917202163", "user_id": "1553194130", "text": "@kachalova_lubov именно «ушедших». В публицистике должно быть больше конкретики. Работая редактором, часто истребляла многоточия.", "type": 2, "created_at": 1553932059, "created_at_utc": 1553932059, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "inline_composer_display_condition": "never"}, {"pk": "18049568809024649", "user_id": "19127031", "text": "Многоточия да! Я в переписке всегда спрашиваю, что вы под этим .... имеете ввиду) больше мне люди так не пишут))", "type": 0, "created_at": 1553937465, "created_at_utc": 1553937465, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "19127031", "username": "tadddddyy", "full_name": "Tatiana Druzhnyaeva", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1879693248769800373_19127031", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 1, "inline_composer_display_condition": "never"}], "comment_count": 6, "caption": {"pk": "17884550440314611", "user_id": "1553194130", "text": "Нет ничего мерзее многоточий", "type": 1, "created_at": 1553930114, "created_at_utc": 1553930114, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false}, "did_report_as_spam": false, "share_enabled": false, "has_translation": true}, "caption_is_edited": true, "has_more_comments": false, "has_more_headload_comments": false, "media_header_display": "none", "display_realtime_typing_indicator": true, "preview_comments": [], "can_view_more_preview_comments": false, "status": "ok"}
)
edges := JSON.Parse(ResponseText).comments
MsgBox % ListValues(edges)
ListValues(edges) {
list := ""
For k, NDS1 in edges {
text := NDS1.text
created_at := NDS1.created_at
pk := NDS1.pk
comment_like_count := NDS1.comment_like_count
user_id := NDS1.user_id
username := NDS1.user.username
full_name := NDS1.user.full_name
profile_pic_id := NDS1.user.profile_pic_id
;profile_pic_url := NDS1.user.profile_pic_url
list .= created_at " `n" text " `n▌" pk " `n" full_name " `n" username " `n" user_id " `n" profile_pic_id " `n" comment_like_count "`n"
} Return list
}
class JSON
{
static JS := JSON._GetJScripObject()
Parse(JsonString) {
try oJSON := this.JS.("(" JsonString ")")
catch {
MsgBox, Wrong JsonString!
Return
}
Return this._CreateObject(oJSON)
}
_GetJScripObject() {
VarSetCapacity(tmpFile, (MAX_PATH := 260) << !!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
}
}
Вот так тоже берется только первый комментарий, а не все поочередно —
loop, 5
{
for k, v in ["text", "created_at", "pk", "comment_like_count", "user_id"]
%v% := JSON.GetKey(sJson, "comments[0]." . v)
for k, v in ["username", "full_name", "profile_pic_id", "profile_pic_url"]
%v% := JSON.GetKey(sJson, "comments[0].user." . v)
comments .= created_at " | " text " | " pk " | " full_name " | " username " | " user_id " | " profile_pic_id " | " comment_like_count " | " profile_pic_url "`n"
}
Как-то так:
sJson =
(
{"comment_likes_enabled": true, "comments": [{"pk": "17940178414272410", "user_id": "10843926123", "text": "А я люблю многоточие. Помнишь, у Набокова:\"Следы на цыпочках ушедших слов\"?", "type": 0, "created_at": 1553931298, "created_at_utc": 1553931298, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "10843926123", "username": "kachalova_lubov", "full_name": "Любовь Качалова. Коуч", "is_private": false, "profile_pic_url": "https", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 3, "inline_composer_display_condition": "never"}, {"pk": "17988326917202163", "user_id": "1553194130", "text": "@kachalova_lubov именно «ушедших». В публицистике должно быть больше конкретики. Работая редактором, часто истребляла многоточия.", "type": 2, "created_at": 1553932059, "created_at_utc": 1553932059, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "inline_composer_display_condition": "never"}, {"pk": "18049568809024649", "user_id": "19127031", "text": "Многоточия да! Я в переписке всегда спрашиваю, что вы под этим .... имеете ввиду) больше мне люди так не пишут))", "type": 0, "created_at": 1553937465, "created_at_utc": 1553937465, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "19127031", "username": "tadddddyy", "full_name": "Tatiana Druzhnyaeva", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1879693248769800373_19127031", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 1, "inline_composer_display_condition": "never"}], "comment_count": 6, "caption": {"pk": "17884550440314611", "user_id": "1553194130", "text": "Нет ничего мерзее многоточий", "type": 1, "created_at": 1553930114, "created_at_utc": 1553930114, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false}, "did_report_as_spam": false, "share_enabled": false, "has_translation": true}, "caption_is_edited": true, "has_more_comments": false, "has_more_headload_comments": false, "media_header_display": "none", "display_realtime_typing_indicator": true, "preview_comments": [], "can_view_more_preview_comments": false, "status": "ok"}
)
comments := ""
for each, comment in JSON.Enum(sJson, "comments") {
for each, key in ["text", "created_at", "pk", "comment_like_count", "user_id"]
%key% := JSON.GetKey(comment, key)
user := JSON.GetKey(comment, "user")
for each, key in ["username", "full_name", "profile_pic_id", "profile_pic_url"]
%key% := JSON.GetKey(user, key)
comments .= (comments ? "`n`n" : "") . "▌`n" . created_at . "`n"
. text . "`n"
. pk . "`n"
. full_name . "`n"
. username . "`n"
. user_id . "`n"
. profile_pic_id . "`n"
. comment_like_count . "`n"
. profile_pic_url
}
MsgBox, % comments
class JSON
{
static JS := JSON._GetJScriptObject()
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false) {
if js
Return this.JS.JSON.stringify(obj)
else {
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ")")
}
}
GetKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
Return res
}
SetKey(sJson, key, value) {
if !(this.VerifyJson(sJson) && this.VerifyJson(value))
Return
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
RemoveKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
sign := SubStr(key, 1, 1) = "[" ? "" : "."
if !RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
else
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
Enum(sJson, key := "") {
if !this.VerifyJson(sJson)
Return
conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
try jsObj := this.JS.eval("(" sJson ")" . conc)
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
res := jsObj.IsArray()
if (res = "")
Return
obj := {}
if (res = -1) {
Loop % jsObj.length
obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
}
else if (res = 0) {
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
}
Return obj
}
VerifyJson(sJson) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
MsgBox, Wrong JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScriptObject() {
static doc
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
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.eval(JScript)
}
_CreateObject(jsObj) {
res := jsObj.IsArray()
if (res = "")
Return jsObj
else if (res = -1) {
obj := []
Loop % jsObj.length
obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
}
Return obj
}
}
Исправлен баг, возникающий в Stringify(), когда в ключах JSON-строки есть пробелы. Добавлен параметр indent в Stringify(), GetKey(), SetKey() и RemoveKey().
sjson = {"supportedDevices":{"Android":4,"iPhone OS":8,"Windows Phone":8}}
obj := JSON.Parse(sjson)
MsgBox, % sjson := JSON.Stringify(obj,, A_Tab)
MsgBox, % JSON.GetKey(sjson, "supportedDevices", " ")
jsObj := JSON.Parse(sjson, true)
MsgBox, % sjson := JSON.Stringify(jsObj, true, " ")
MsgBox, % sjson := JSON.SetKey(sjson, "accounts", "[{'email':'dfiveg@mail.ru','authorized':false,'lost':true}]", " ")
MsgBox, % JSON.RemoveKey(sjson, "accounts[0].email", " ")
class JSON
{
static JS := JSON._GetJScriptObject()
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false, indent := "") {
if js
Return this.JS.JSON.stringify(obj, "", indent)
else {
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
}
}
GetKey(sJson, key, indent := "") {
if !this.VerifyJson(sJson)
Return
try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ",'','" . indent . "')")
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
Return res
}
SetKey(sJson, key, value, indent := "") {
if !(this.VerifyJson(sJson) && this.VerifyJson(value))
Return
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
. "JSON.stringify(obj,'','" . indent . "')" )
this.JS.eval("obj = ''")
Return res
}
RemoveKey(sJson, key, indent := "") {
if !this.VerifyJson(sJson)
Return
sign := SubStr(key, 1, 1) = "[" ? "" : "."
if !RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj,'','" . indent . "')")
else
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
. "JSON.stringify(obj,'','" . indent . "')" )
this.JS.eval("obj = ''")
Return res
}
Enum(sJson, key := "") {
if !this.VerifyJson(sJson)
Return
conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
try jsObj := this.JS.eval("(" sJson ")" . conc)
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
res := jsObj.IsArray()
if (res = "")
Return
obj := {}
if (res = -1) {
Loop % jsObj.length
obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
}
else if (res = 0) {
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
}
Return obj
}
VerifyJson(sJson) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
MsgBox, Wrong JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScriptObject() {
static doc
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
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.eval(JScript)
}
_CreateObject(jsObj) {
res := jsObj.IsArray()
if (res = "")
Return jsObj
else if (res = -1) {
obj := []
Loop % jsObj.length
obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
}
Return obj
}
}
Исправлен баг, возникающий в Stringify(), когда в ключах JSON-строки есть пробелы.
Исправил эту же проблему в Enum(), добавил в Enum() параметр indent, добавил поддержку true, false и null (нужно использовать JSON.true, JSON.false, JSON.null).
obj := { supportedDevices: {Android: JSON.true, "iPhone OS": JSON.false, "Windows Phone": JSON.null} }
MsgBox, % sjson := JSON.Stringify(obj,, " ")
sjson := JSON.SetKey(sjson, "accounts", "[{'email':'dfiveg@mail.ru','authorized':false,'lost':true}]")
for k, v in JSON.Enum(sjson,, " ")
MsgBox, % "key:`n" . k . "`n`nvalue:`n" . v
class JSON
{
static JS := JSON._GetJScriptObject(), true := {}, false := {}, null := {}
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false, indent := "") {
if (js && !RegExMatch(js, "\s"))
Return this.JS.JSON.stringify(obj, "", indent)
else {
(RegExMatch(js, "\s") && indent := js)
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
}
}
GetKey(sJson, key, indent := "") {
if !this.VerifyJson(sJson)
Return
try Return this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ",'','" . indent . "')")
catch
MsgBox, Bad key:`n`n%key%
}
SetKey(sJson, key, value, indent := "") {
if !this.VerifyJson(sJson)
Return
if !this.VerifyJson(value, true) {
MsgBox, % "Bad value: " . value . "`n"
. "Must be a valid JSON string." . "`n"
. "Enclose literal strings in quotes '' or """".`n"
. "As an empty string pass '' or """""
Return
}
try {
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
. "JSON.stringify(obj,'','" . indent . "')" )
this.JS.eval("obj = ''")
Return res
}
catch
MsgBox, Bad key:`n`n%key%
}
RemoveKey(sJson, key, indent := "") {
if !this.VerifyJson(sJson)
Return
sign := SubStr(key, 1, 1) = "[" ? "" : "."
try {
if !RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj,'','" . indent . "')")
else
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
. "JSON.stringify(obj,'','" . indent . "')" )
this.JS.eval("obj = ''")
Return res
}
catch
MsgBox, Bad key:`n`n%key%
}
Enum(sJson, key := "", indent := "") {
if !this.VerifyJson(sJson)
Return
conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
try {
jsObj := this.JS.eval("(" sJson ")" . conc)
res := jsObj.IsArray()
if (res = "")
Return
obj := {}
if (res = -1) {
Loop % jsObj.length
obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "],'','" . indent . "')")
}
else if (res = 0) {
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "['" . k . "'],'','" . indent . "')")
}
Return obj
}
catch
MsgBox, Bad key:`n`n%key%
}
VerifyJson(sJson, silent := false) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
if !silent
MsgBox, Bad JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
if IsObject( obj ) {
for k, v in ["true", "false", "null"]
if (obj = this[v])
Return v
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._ObjToString(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] )
Return """" obj """"
}
_GetJScriptObject() {
static doc
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
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.eval(JScript)
}
_CreateObject(jsObj) {
res := jsObj.IsArray()
if (res = "")
Return jsObj
else if (res = -1) {
obj := []
Loop % jsObj.length
obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
}
Return obj
}
}
Сообщайте о замеченных багах.
Исправлен баг с парсингом спецсимволов, добавлена обработка ошибок.
teadrinker, если ты привяжешь свою библиотеку к chrome.ahk, то можно будет хорошо потестировать её, сравнивая результат получения страниц через селениум и через твою.
Что ты имеешь в виду под привязкой? Привязкой можно считать этот код.
Под привязкой я имею в виду, чтобы при выполнении такого кода использовалась твоя библиотека:
ChromeInst := new Chrome()
PageInst := ChromeInst.GetPage()
PageInst.Call("Page.navigate", {"url": "http://delfi.lv"})
PageInst.WaitForLoad()
RootNode := PageInst.Call("DOM.getDocument").root
OuterHTML := PageInst.Call("DOM.getOuterHTML", {"nodeId": RootNode.nodeId}).OuterHTML
Так у меня работает. Я только путь к профилю ещё указываю.
В твоем коде если использовать JS версии IE > 9:
js := new ActiveScript("{16d51579-a30b-4c8b-a276-0ff4dc41e755}")
выдает ошибку:
ahkObj := { ключ: [{ключ: "значение"}, {ключ: true}] }
MsgBox, % JSON.Stringify(ahkObj)
/*
* ActiveScript for AutoHotkey v1.1
*
* Provides an interface to Active Scripting languages like VBScript and JScript,
* without relying on Microsoft's ScriptControl, which is not available to 64-bit
* programs.
*
* License: Use, modify and redistribute without limitation, but at your own risk.
*/
class ActiveScript extends ActiveScript._base
{
__New(Language)
{
if this._script := ComObjCreate(Language, ActiveScript.IID)
this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
if !this._scriptParse
throw Exception("Invalid language", -1, Language)
this._site := new ActiveScriptSite(this)
this._SetScriptSite(this._site.ptr)
this._InitNew()
this._objects := {}
this.Error := ""
this._dsp := this._GetScriptDispatch() ; Must be done last.
try
if this.ScriptEngine() = "JScript"
this.SetJScript58()
}
SetJScript58()
{
static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
return false
VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
, "ptr", 0, "ptr", &var), ObjRelease(prop)
return hr >= 0
}
Eval(Code)
{
pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
this._ParseScriptText(Code, 0x20, pvar) ; SCRIPTTEXT_ISEXPRESSION := 0x20
return arr[0]
}
Exec(Code)
{
this._ParseScriptText(Code, 0x42, 0) ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
this._SetScriptState(2) ; SCRIPTSTATE_CONNECTED := 2
}
AddObject(Name, DispObj, AddMembers := false)
{
static a, supports_dispatch ; Test for built-in IDispatch support.
:= a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
this._objects[Name] := DispObj
this._AddNamedItem(Name, AddMembers ? 8 : 2) ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
}
_GetObjectUnk(Name)
{
return !IsObject(dsp := this._objects[Name]) ? dsp ; Pointer
: ComObjValue(dsp) ? ComObjValue(dsp) ; ComObject
: &dsp ; AutoHotkey object
}
class _base
{
__Call(Method, Params*)
{
if ObjHasKey(this, "_dsp")
try
return (this._dsp)[Method](Params*)
catch e
throw Exception(e.Message, -1, e.Extra)
}
__Get(Property, Params*)
{
if ObjHasKey(this, "_dsp")
try
return (this._dsp)[Property, Params*]
catch e
throw Exception(e.Message, -1, e.Extra)
}
__Set(Property, Params*)
{
if ObjHasKey(this, "_dsp")
{
Value := Params.Pop()
try
return (this._dsp)[Property, Params*] := Value
catch e
throw Exception(e.Message, -1, e.Extra)
}
}
}
_SetScriptSite(Site)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
if (hr < 0)
this._HRFail(hr, "IActiveScript::SetScriptSite")
}
_SetScriptState(State)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
if (hr < 0)
this._HRFail(hr, "IActiveScript::SetScriptState")
}
_AddNamedItem(Name, Flags)
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
if (hr < 0)
this._HRFail(hr, "IActiveScript::AddNamedItem")
}
_GetScriptDispatch()
{
hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
if (hr < 0)
this._HRFail(hr, "IActiveScript::GetScriptDispatch")
return ComObject(9, pdsp, 1)
}
_InitNew()
{
hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
if (hr < 0)
this._HRFail(hr, "IActiveScriptParse::InitNew")
}
_ParseScriptText(Code, Flags, pvarResult)
{
VarSetCapacity(excp, 8 * A_PtrSize, 0)
hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
, "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
, "uint", Flags, "ptr", pvarResult, "ptr", 0)
if (hr < 0)
this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
}
_HRFail(hr, what)
{
if e := this.Error
{
this.Error := ""
throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
. "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
. "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
. "`nLine text:`t`t" e.LineText, -3)
}
throw Exception(what " failed with code " this._HRFormat(hr), -2)
}
_HRFormat(hr)
{
return Format("0x{1:X}", hr & 0xFFFFFFFF)
}
_OnScriptError(err) ; IActiveScriptError err
{
VarSetCapacity(excp, 8 * A_PtrSize, 0)
DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
DllCall(fn, "ptr", &excp)
wcode := NumGet(excp, 0, "ushort")
hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
static Infos := "Source,Description,HelpFile"
Loop Parse, % Infos, `,
if pbstr := NumGet(excp, A_Index * A_PtrSize)
this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
}
__Delete()
{
if this._script
{
DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p) ; Close
ObjRelease(this._script)
}
if this._scriptParse
ObjRelease(this._scriptParse)
}
static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}
class ActiveScriptSite
{
__New(Script)
{
ObjSetCapacity(this, "_site", 3 * A_PtrSize)
NumPut(&Script
, NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
, NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
, this.ptr := ObjGetAddress(this, "_site"))))
}
_vftable(Name, PrmCounts, EIBase)
{
if p := ObjGetAddress(this, Name)
return p
ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
p := ObjGetAddress(this, Name)
Loop Parse, % PrmCounts
{
cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
NumPut(cb, p + (A_Index-1) * A_PtrSize)
}
return p
}
}
_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
Method := A_EventInfo & 0xFF
if A_EventInfo >= 0x100 ; IActiveScriptSiteWindow
{
if Method = 4 ; GetWindow
{
NumPut(0, a1+0) ; *phwnd := 0
return 0 ; S_OK
}
if Method = 5 ; EnableModeless
{
return 0 ; S_OK
}
this -= A_PtrSize ; Cast to IActiveScriptSite
}
;else: IActiveScriptSite
if Method = 1 ; QueryInterface
{
iid := _AS_GUIDToString(a1)
if (iid = "{00000000-0000-0000-C000-000000000046}" ; IUnknown
|| iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
{
NumPut(this, a2+0)
return 0 ; S_OK
}
if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
{
NumPut(this + A_PtrSize, a2+0)
return 0 ; S_OK
}
NumPut(0, a2+0)
return 0x80004002 ; E_NOINTERFACE
}
if Method = 5 ; GetItemInfo
{
a1 := StrGet(a1, "UTF-16")
, (a3 && NumPut(0, a3+0)) ; *ppiunkItem := NULL
, (a4 && NumPut(0, a4+0)) ; *ppti := NULL
if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
{
if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
ObjAddRef(unk), NumPut(unk, a3+0)
}
return 0 ; S_OK
}
if Method = 9 ; OnScriptError
return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
; AddRef and Release don't do anything because we want to avoid circular references.
; The site and IActiveScript are both released when the AHK script releases its last
; reference to the ActiveScript object.
; All of the other methods don't require implementations.
return 0x80004001 ; E_NOTIMPL
}
_AS_GUIDToString(pGUID)
{
VarSetCapacity(String, 38*2)
DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
return String
}
class JSON
{
static JS := JSON._GetJScriptObject()
Parse(sJson, js := false) {
if jsObj := this.VerifyJson(sJson)
Return js ? jsObj : this._CreateObject(jsObj)
}
Stringify(obj, js := false) {
if js
Return this.JS.JSON.stringify(obj)
else {
sObj := this._ObjToString(obj)
Return this.JS.eval("JSON.stringify(" . sObj . ")")
}
}
GetKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
Return res
}
SetKey(sJson, key, value) {
if !(this.VerifyJson(sJson) && this.VerifyJson(value))
Return
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
RemoveKey(sJson, key) {
if !this.VerifyJson(sJson)
Return
sign := SubStr(key, 1, 1) = "[" ? "" : "."
if !RegExMatch(key, "(.*)\[(\d+)]$", match)
res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
else
res := this.JS.eval( "var obj = (" . sJson . ");"
. "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
. "JSON.stringify(obj)" )
this.JS.eval("obj = ''")
Return res
}
Enum(sJson, key := "") {
if !this.VerifyJson(sJson)
Return
conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
try jsObj := this.JS.eval("(" sJson ")" . conc)
catch {
MsgBox, Wrong key:`n`n%key%
Return
}
res := jsObj.IsArray()
if (res = "")
Return
obj := {}
if (res = -1) {
Loop % jsObj.length
obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
}
else if (res = 0) {
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
}
Return obj
}
VerifyJson(sJson) {
try jsObj := this.JS.eval("(" sJson ")")
catch {
MsgBox, Wrong JSON string:`n`n%sJson%
Return
}
Return IsObject(jsObj) ? jsObj : true
}
_ObjToString(obj) {
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._ObjToString(v)
Return isArray ? "[" str "]" : "{" str "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "\s"))
Return obj
else
Return """" obj """"
}
_GetJScriptObject() {
JS := new ActiveScript("{16d51579-a30b-4c8b-a276-0ff4dc41e755}")
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.eval(JScript)
}
_CreateObject(jsObj) {
res := jsObj.IsArray()
if (res = "")
Return jsObj
else if (res = -1) {
obj := []
Loop % jsObj.length
obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
}
else if (res = 0) {
obj := {}
keys := jsObj.GetKeys()
Loop % keys.length
k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
}
Return obj
}
}
Ничего не понял. А зачем использовать IE > 9 ?
Решил проверить может уберется конвертация символов.
Сейчас у тебя работает на движке 5.8.
JS := new ActiveScript("JScript")
MsgBox % js.ScriptEngineMajorVersion() "." js.ScriptEngineMinorVersion()
Почему?
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
MsgBox, % doc.documentMode
Я имею в виду в коде 44.
А что это за CLSID? Почему он будет запускать версию > 9? Я у себя в реестре вижу соответствие ему
ProgID: JScript
InprocServer32: C:\Windows\System32\jscript9.dll
В любом случае так мы используем JScript, а в моём коде чистый Javascript.
; Show which scripting engine is in use; probably "5.8".
ShowVersion(js)
; To use the newer IE9+ version of JScript, use the following.
js := new ActiveScript("{16d51579-a30b-4c8b-a276-0ff4dc41e755}")
ShowVersion(js)
ShowVersion(as) {
MsgBox % as.ScriptEngineMajorVersion() "." as.ScriptEngineMinorVersion()
}
А, ну значит в этой версии JScript объект JSON не определён по какой-то причине.
Через htmlfile работает:
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=11"">")
JS := doc.parentWindow
MsgBox, % JS.eval("JSON.stringify({key:'value'})")
Наверное, действительно, это версия урезанная.
Если через ActiveScript получать, то надо использовать JsRT.IE или JsRT.Edge.
teadrinker, так-как json может быть опасным, предлагаю проверять его на наличие опасного кода по алгоритму json2.js.
https://github.com/douglascrockford/JSO … n2.js#L473
Думаешь, в контексте AHK в этом есть смысл? Не логичнее просто не принимать json из непроверенных источников?
Думаешь, в контексте AHK в этом есть смысл?
Не совсем понял, что ты имеешь в виду под контекстом AHK.
Я, конечно, не вирусописатель, но при "правильных" настройках IE, можно неприятных вещей натворить.
Не логичнее просто не принимать json из непроверенных источников?
А разве сейчас можно кому-либо на 100% доверять?
Имею в виду, что для AHK использование JSON, полученного из интернета, не такая частая ситуация, это же не web-разработка.
А разве сейчас можно кому-либо на 100% доверять?
Не доверяешь — не запускай. Я постоянно пользуюсь JSON только из Гугл-транслейта, и то его не запускаю, а разбираю парсингом. За весь свой опыт не помню ситуации, чтобы был риск запустить из JSON вредоносный код. Можешь привести пример таковой?
Кстати, у нас же было изначально
JS.("delete ActiveXObject; delete GetObject;")
Это не решает проблему?
Чтобы отправить ответ, вы должны войти или зарегистрироваться