;;;; AHK v2 ; Example =================================================================================== ; =========================================================================================== ; Msgbox "The idea here is to create several nested arrays, save to text with jxon_dump(), and then reload the array with jxon_load(). The resulting array should be the same.`r`n`r`nThis is what this example shows." ; a := Map(), b := Map(), c := Map(), d := Map(), e := Map(), f := Map() ; Object() is more technically correct than {} but both will work. ; d["g"] := 1, d["h"] := 2, d["i"] := ["purple","pink","pippy red"] ; e["g"] := 1, e["h"] := 2, e["i"] := Map("1","test1","2","test2","3","test3") ; f["g"] := 1, f["h"] := 2, f["i"] := [1,2,Map("a",1.0009,"b",2.0003,"c",3.0001)] ; a["test1"] := "test11", a["d"] := d ; b["test3"] := "test33", b["e"] := e ; c["test5"] := "test55", c["f"] := f ; myObj := Map() ; myObj["a"] := a, myObj["b"] := b, myObj["c"] := c, myObj["test7"] := "test77", myObj["test8"] := "test88" ; g := ["blue","green","red"], myObj["h"] := g ; add linear array for testing ; q := Chr(34) ; textData2 := Jxon_dump(myObj,4) ; ===> convert array to JSON ; msgbox "JSON output text:`r`n===========================================`r`n(Should match second output.)`r`n`r`n" textData2 ; newObj := Jxon_load(&textData2) ; ===> convert json back to array ; textData3 := Jxon_dump(newObj,4) ; ===> break down array into 2D layout again, should be identical ; msgbox "Second output text:`r`n===========================================`r`n(should be identical to first output)`r`n`r`n" textData3 ; msgbox "textData2 = textData3: " ((textData2=textData3) ? "true" : "false") ; =========================================================================================== ; End Example ; ============================================================================= ; =========================================================================================== ; originally posted by user coco on AutoHotkey.com ; https://github.com/cocobelgica/AutoHotkey-JSON Jxon_Load(&src, args*) { key := "", is_key := false stack := [ tree := [] ] next := '"{[01234567890-tfn' pos := 0 while ( (ch := SubStr(src, ++pos, 1)) != "" ) { if InStr(" `t`n`r", ch) continue if !InStr(next, ch, true) { testArr := StrSplit(SubStr(src, 1, pos), "`n") ln := testArr.Length col := pos - InStr(src, "`n",, -(StrLen(src)-pos+1)) msg := Format("{}: line {} col {} (char {})" , (next == "") ? ["Extra data", ch := SubStr(src, pos)][1] : (next == "'") ? "Unterminated string starting at" : (next == "\") ? "Invalid \escape" : (next == ":") ? "Expecting ':' delimiter" : (next == '"') ? "Expecting object key enclosed in double quotes" : (next == '"}') ? "Expecting object key enclosed in double quotes or object closing '}'" : (next == ",}") ? "Expecting ',' delimiter or object closing '}'" : (next == ",]") ? "Expecting ',' delimiter or array closing ']'" : [ "Expecting JSON value(string, number, [true, false, null], object or array)" , ch := SubStr(src, pos, (SubStr(src, pos)~="[\]\},\s]|$")-1) ][1] , ln, col, pos) throw Error(msg, -1, ch) } obj := stack[1] is_array := (obj is Array) if i := InStr("{[", ch) { ; start new object / map? val := (i = 1) ? Map() : Array() ; ahk v2 is_array ? obj.Push(val) : obj[key] := val stack.InsertAt(1,val) next := '"' ((is_key := (ch == "{")) ? "}" : "{[]0123456789-tfn") } else if InStr("}]", ch) { stack.RemoveAt(1) next := (stack[1]==tree) ? "" : (stack[1] is Array) ? ",]" : ",}" } else if InStr(",:", ch) { is_key := (!is_array && ch == ",") next := is_key ? '"' : '"{[0123456789-tfn' } else { ; string | number | true | false | null if (ch == '"') { ; string i := pos while i := InStr(src, '"',, i+1) { val := StrReplace(SubStr(src, pos+1, i-pos-1), "\\", "\u005C") if (SubStr(val, -1) != "\") break } if !i ? (pos--, next := "'") : 0 continue pos := i ; update pos val := StrReplace(val, "\/", "/") val := StrReplace(val, '\"', '"') , val := StrReplace(val, "\b", "`b") , val := StrReplace(val, "\f", "`f") , val := StrReplace(val, "\n", "`n") , val := StrReplace(val, "\r", "`r") , val := StrReplace(val, "\t", "`t") i := 0 while i := InStr(val, "\",, i+1) { if (SubStr(val, i+1, 1) != "u") ? (pos -= StrLen(SubStr(val, i)), next := "\") : 0 continue 2 xxxx := Abs("0x" . SubStr(val, i+2, 4)) ; \uXXXX - JSON unicode escape sequence if (xxxx < 0x100) val := SubStr(val, 1, i-1) . Chr(xxxx) . SubStr(val, i+6) } if is_key { key := val, next := ":" continue } } else { ; number | true | false | null val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos) if IsInteger(val) val += 0 else if IsFloat(val) val += 0 else if (val == "true" || val == "false") val := (val == "true") else if (val == "null") val := "" else if is_key { pos--, next := "#" continue } pos += i-1 } is_array ? obj.Push(val) : obj[key] := val next := obj == tree ? "" : is_array ? ",]" : ",}" } } return tree[1] } Jxon_Dump(obj, indent:="", lvl:=1) { if IsObject(obj) { If !(obj is Array || obj is Map || obj is String || obj is Number) throw Error("Object type not supported.", -1, Format("", ObjPtr(obj))) if IsInteger(indent) { if (indent < 0) throw Error("Indent parameter must be a postive integer.", -1, indent) spaces := indent, indent := "" Loop spaces ; ===> changed indent .= " " } indt := "" Loop indent ? lvl : 0 indt .= indent is_array := (obj is Array) lvl += 1, out := "" ; Make #Warn happy for k, v in obj { if IsObject(k) || (k == "") throw Error("Invalid object key.", -1, k ? Format("", ObjPtr(obj)) : "") if !is_array ;// key ; ObjGetCapacity([k], 1) out .= (ObjGetCapacity([k]) ? Jxon_Dump(k) : escape_str(k)) (indent ? ": " : ":") ; token + padding out .= Jxon_Dump(v, indent, lvl) ; value . ( indent ? ",`n" . indt : "," ) ; token + indent } if (out != "") { out := Trim(out, ",`n" . indent) if (indent != "") out := "`n" . indt . out . "`n" . SubStr(indt, StrLen(indent)+1) } return is_array ? "[" . out . "]" : "{" . out . "}" } Else If (obj is Number) return obj Else ; String return escape_str(obj) escape_str(obj) { obj := StrReplace(obj,"\","\\") obj := StrReplace(obj,"`t","\t") obj := StrReplace(obj,"`r","\r") obj := StrReplace(obj,"`n","\n") obj := StrReplace(obj,"`b","\b") obj := StrReplace(obj,"`f","\f") obj := StrReplace(obj,"/","\/") obj := StrReplace(obj,'"','\"') return '"' obj '"' } }