Save the JSON sample below as settings.json and the class as Class.JSONData.ahk, both in the script's own directory.
manifest.json:
Code: Select all
{
"[AA]BB[CC]":"test\ttest\r\ntest\ttest"
,"parameters": [ 0.3, 22, false, false, null ]
,"some key":19
,"hotkeys": {
"keyboard": {
"hotkeys": {
"a":"!g"
,"b":"!q"
,"c":"!s"
,"d":"!x"
,"e":"!NumpadAdd"
,"f":"!NumpadSub"
}
}
,"joystick": {
"port":0
,"parameter":0.3
,"hotkeys": {
"a":"Joy12"
,"b":"Joy11"
,"c":"Joy10"
,"d":"Joy1"
,"e":"Joy6"
,"f":"Joy5"
}
}
}
,"options": {
"outputDebug":false
}
}
JSONData class:
Code: Select all
Class JSONData {
Init() {
static __ := JSONData.Init()
__HTMLFile := "<!DOCTYPE html>"
. "<html>"
. "<head>"
. "<meta http-equiv=""X-UA-Compatible"" content=""IE=edge"">"
. "<meta charset=""utf-8"" />"
. "<title>HTMLFile</title>"
. "<script>"
. "var _null = null;"
. "var JSONData = new Array();"
. "function _delete(__o, __k) {"
. "delete __o[__k];"
. "}"
. "</script>"
. "</head>"
. "<body>"
. "</body>"
. "</html>"
(JSONData.oHTML:=ComObjCreate("HTMLFile")).write(__HTMLFile)
}
__New(__fileFullPath, __restore:=false) {
static __i := -1
if not (__f:=FileOpen(this.fileFullPath:=__fileFullPath, "r", "utf-8"))
return false, ErrorLevel := (A_LastError == 2) ? "ERROR_FILE_NOT_FOUND" : (A_LastError == 3) ? "ERROR_PATH_NOT_FOUND" : A_LastError ; https://msdn.microsoft.com/fr-fr/library/windows/desktop/ms681382(v=vs.85).aspx
try {
(JSONData.oHTML.parentWindow.JSONData)[ (__restore) ? this.index : this.index:=++__i ] := JSONData.parse(__f.Read())
} catch {
__f.Close()
return false, ErrorLevel:="ERROR_PARSE_ERROR"
}
__f.Close()
return this
}
Class Enumerator {
i := -1
__New(__object) {
try this.count := (this.keys:=JSONData.oHTML.parentWindow.Object.keys(this.object:=__object).slice()).length
}
next(ByRef __k:="", ByRef __v:="") {
if (++this.i < this.count) {
__k := (this.keys)[ this.i ], __v := (this.object)[__k]
return true
} return false, this.i:=-1
}
}
stringify(__obj, __space:="") {
return JSONData.oHTML.parentWindow.JSON.stringify(__obj,, __space)
}
parse(__str) {
return JSONData.oHTML.parentWindow.JSON.parse(__str)
}
delete(__o, __k) {
JSONData.oHTML.parentWindow._delete(__o, __k)
}
; ___________________________________________
Class String {
__New() {
return JSONData.oHTML.parentWindow.String()
}
}
Class Number {
__New() {
return JSONData.oHTML.parentWindow.Number()
}
}
Class Object {
__New() {
return JSONData.oHTML.parentWindow.Object()
}
}
Class Array {
__New() {
return JSONData.oHTML.parentWindow.Array()
}
}
; ___________________________________________
restore() {
this := this.__New(this.fileFullPath, true)
}
updateData(__prettify:=4) {
(__f:=FileOpen(this.fileFullPath, "w", "utf-8")).Write(JSONData.stringify(this.__data__, __prettify))
__f.Close()
}
__data__[__args*] {
get {
return (JSONData.oHTML.parentWindow.JSONData)[ this.index ]
}
set {
(JSONData.oHTML.parentWindow.JSONData)[ this.index ] := value
}
}
getKeys(__prm*) {
__o := this.__data__
for __k, __v in __prm
__o := (__o)[__v]
return JSONData.oHTML.parentWindow.Object.getOwnPropertyNames(__o)
}
}
Example:
Code: Select all
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#Warn ; Enable warnings to assist with detecting common errors.
#SingleInstance, force
#Include %A_ScriptDir%\Class.JSONData.ahk
SETTINGS_FILE := A_ScriptDir . "\settings.json"
Settings := new JSONData(SETTINGS_FILE) ; create a new instance of JSONData base object
if (ErrorLevel) { ; specifically, ErrorLevel is set to 'ERROR_FILE_NOT_FOUND' if the file doesn't exist and to 'ERROR_PARSE_ERROR' if the file is badly formatted
MsgBox, 64,, % "Could not read or parse: " . SETTINGS_FILE . "`r`nSpecifically: " . ErrorLevel . "`r`n`r`nThe program will exit."
ExitApp
}
data := Settings.__data__ ; get a reference to the data themselves
; =========== retrieve a specific datum ===========
MsgBox % JSONData.stringify(data, A_Tab) ; displays all data as a string using the stringify base method; A_Tab will insert a tab character into the output JSON string for readability purposes
MsgBox % data.options.outputDebug ; the dot is the easiest way to retrieve a specific datum
MsgBox % (data.parameters)[0] ; using brackets, the parenthesis are required when used in combination with the dot syntax; also, indexes start with 0
MsgBox % (data.parameters).join("~") ; each time an array or object is retrieved it is question of the javascript one so one can operate upon it using javascript methods supported by the type of the value retrieved
MsgBox % data["some key"] ; if key contains spaces, it must be enclosed in brackets
MsgBox % data["[AA]BB[CC]"] ; brackets are here required to avoid an ambiguous use of the dot > .[
; =========== operate upon data using a enumerator object ===========
enum := new JSONData.Enumerator(data.hotkeys.keyboard.hotkeys) ; enumerator object similar to the ahk one, usefull to enumerate all items in a collection
while enum.next(k, v)
MsgBox % k . "," . v
; =========== delete data ===========
JSONData.delete(data, "hotkeys") ; let's delete some key using the delete base method
MsgBox % JSONData.stringify(data) ; displays data as a string to see changes
Settings.restore() ; undo changes
data := Settings.__data__ ; restore data which still contains the previous data
MsgBox % JSONData.stringify(data)
; =========== retrieve keys ===========
keys := Settings.getKeys("hotkeys", "keyboard", "hotkeys") ; get all the key which belongs to an associative array using the getKeys instance method
MsgBox % keys.join("|") ; one can use the join javascript method upon string values (usefull to populate a DropDownList from a settings file for example)
Loop % keys.length ; one must use javascript length property and not the eponymous ahk method since getKeys return a javascript array
{
MsgBox % keys[ a_index - 1 ] ; same as above: indexes start with 0
}
; =========== create and save various type of data ===========
if (Settings.getKeys().indexOf("test_array_of_objects") = -1) ; if the key 'test_array_of_objects' still not exists yet...
data.test_array_of_objects := new JSONData.Array() ; ...create a new array
obj := new JSONData.Object() ; create a new object
obj.a := "some text"
obj.b := 3
obj.c := ""
obj.d := false
obj.e := new JSONData.Object()
obj.f := new JSONData.Array()
data.test_array_of_objects.push(obj) ; appends the object to the array
Settings.updateData() ; updates the file
run, notepad %SETTINGS_FILE% ; run the files to see changes