158 lines
7.4 KiB
AutoHotkey
158 lines
7.4 KiB
AutoHotkey
/************************************************************************
|
|
* @Mdodified by G0nzo
|
|
* @description: JSON格式字符串序列化和反序列化, 修改自[HotKeyIt/Yaml](https://github.com/HotKeyIt/Yaml)
|
|
* 增加了对true/false/null类型的支持, 保留了数值的类型
|
|
* @author thqby, HotKeyIt
|
|
* @date 2023/05/12
|
|
* @version 1.0.5
|
|
***********************************************************************/
|
|
|
|
class JSON {
|
|
static null := ComValue(1, 0), true := ComValue(0xB, 1), false := ComValue(0xB, 0)
|
|
|
|
/**
|
|
* Converts a AutoHotkey Object Notation JSON string into an object.
|
|
* @param text A valid JSON string.
|
|
* @param keepbooltype convert true/false/null to JSON.true / JSON.false / JSON.null where it's true, otherwise 1 / 0 / ''
|
|
* @param as_map object literals are converted to map, otherwise to object
|
|
*/
|
|
static Load(text, keepbooltype := false, as_map := true) {
|
|
keepbooltype ? (_true := JSON.true, _false := JSON.false, _null := JSON.null) : (_true := true, _false := false, _null := "")
|
|
as_map ? (map_set := (maptype := Map).Prototype.Set) : (map_set := (obj, key, val) => obj.%key% := val, maptype := Object)
|
|
NQ := "", LF := "", LP := 0, P := "", R := ""
|
|
D := [C := (A := InStr(text := LTrim(text, " `t`r`n"), "[") = 1) ? [] : maptype()], text := LTrim(SubStr(text, 2), " `t`r`n"), L := 1, N := 0, V := K := "", J := C, !(Q := InStr(text, '"') != 1) ? text := LTrim(text, '"') : ""
|
|
Loop Parse text, '"' {
|
|
Q := NQ ? 1 : !Q
|
|
NQ := Q && (SubStr(A_LoopField, -3) = "\\\" || (SubStr(A_LoopField, -1) = "\" && SubStr(A_LoopField, -2) != "\\"))
|
|
if !Q {
|
|
if (t := Trim(A_LoopField, " `t`r`n")) = "," || (t = ":" && V := 1)
|
|
continue
|
|
else if t && (InStr("{[]},:", SubStr(t, 1, 1)) || RegExMatch(t, "^-?\d*(\.\d*)?\s*[,\]\}]")) {
|
|
Loop Parse t {
|
|
if N && N--
|
|
continue
|
|
if InStr("`n`r `t", A_LoopField)
|
|
continue
|
|
else if InStr("{[", A_LoopField) {
|
|
if !A && !V
|
|
throw Error("Malformed JSON - missing key.", 0, t)
|
|
C := A_LoopField = "[" ? [] : maptype(), A ? D[L].Push(C) : map_set(D[L], K, C), D.Has(++L) ? D[L] := C : D.Push(C), V := "", A := Type(C) = "Array"
|
|
continue
|
|
} else if InStr("]}", A_LoopField) {
|
|
if !A && V
|
|
throw Error("Malformed JSON - missing value.", 0, t)
|
|
else if L = 0
|
|
throw Error("Malformed JSON - to many closing brackets.", 0, t)
|
|
else C := --L = 0 ? "" : D[L], A := Type(C) = "Array"
|
|
} else if !(InStr(" `t`r,", A_LoopField) || (A_LoopField = ":" && V := 1)) {
|
|
if RegExMatch(SubStr(t, A_Index), "m)^(null|false|true|-?\d+\.?\d*)\s*[,}\]\r\n]", &R) && (N := R.Len(0) - 2, R := R.1, 1) {
|
|
if A
|
|
C.Push(R = "null" ? _null : R = "true" ? _true : R = "false" ? _false : IsNumber(R) ? R + 0 : R)
|
|
else if V
|
|
map_set(C, K, R = "null" ? _null : R = "true" ? _true : R = "false" ? _false : IsNumber(R) ? R + 0 : R), K := V := ""
|
|
else throw Error("Malformed JSON - missing key.", 0, t)
|
|
} else {
|
|
; Added support for comments without '"'
|
|
if A_LoopField == '/' {
|
|
nt := SubStr(t, A_Index + 1, 1), N := 0
|
|
if nt == '/' {
|
|
if nt := InStr(t, '`n', , A_Index + 2)
|
|
N := nt - A_Index - 1
|
|
} else if nt == '*' {
|
|
if nt := InStr(t, '*/', , A_Index + 2)
|
|
N := nt + 1 - A_Index
|
|
} else nt := 0
|
|
if N
|
|
continue
|
|
}
|
|
throw Error("Malformed JSON - unrecognized character-", 0, A_LoopField " in " t)
|
|
}
|
|
}
|
|
}
|
|
} else if InStr(t, ':') > 1
|
|
throw Error("Malformed JSON - unrecognized character-", 0, SubStr(t, 1, 1) " in " t)
|
|
} else if NQ && (P .= A_LoopField '"', 1)
|
|
continue
|
|
else if A
|
|
LF := P A_LoopField, C.Push(InStr(LF, "\") ? UC(LF) : LF), P := ""
|
|
else if V
|
|
LF := P A_LoopField, map_set(C, K, InStr(LF, "\") ? UC(LF) : LF), K := V := P := ""
|
|
else
|
|
LF := P A_LoopField, K := InStr(LF, "\") ? UC(LF) : LF, P := ""
|
|
}
|
|
return J
|
|
UC(S, e := 1) {
|
|
static m := Map(Ord('"'), '"', Ord("a"), "`a", Ord("b"), "`b", Ord("t"), "`t", Ord("n"), "`n", Ord("v"), "`v", Ord("f"), "`f", Ord("r"), "`r")
|
|
local v := ""
|
|
Loop Parse S, "\"
|
|
if !((e := !e) && A_LoopField = "" ? v .= "\" : !e ? (v .= A_LoopField, 1) : 0)
|
|
v .= (t := InStr("ux", SubStr(A_LoopField, 1, 1)) ? SubStr(A_LoopField, 1, RegExMatch(A_LoopField, "i)^[ux]?([\dA-F]{4})?([\dA-F]{2})?\K") - 1) : "") && RegexMatch(t, "i)^[ux][\da-f]+$") ? Chr(Abs("0x" SubStr(t, 2))) SubStr(A_LoopField, RegExMatch(A_LoopField, "i)^[ux]?([\dA-F]{4})?([\dA-F]{2})?\K")) : m.has(Ord(A_LoopField)) ? m[Ord(A_LoopField)] SubStr(A_LoopField, 2) : "\" A_LoopField, e := A_LoopField = "" ? e : !e
|
|
return v
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts a AutoHotkey Array/Map/Object to a Object Notation JSON string.
|
|
* @param obj A AutoHotkey value, usually an object or array or map, to be converted.
|
|
* @param expandlevel The level of JSON string need to expand, by default expand all.
|
|
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
|
*/
|
|
static Dump(obj, expandlevel := unset, space := " ") {
|
|
expandlevel := IsSet(expandlevel) ? Abs(expandlevel) : 10000000
|
|
return Trim(CO(obj, expandlevel))
|
|
CO(O, J := 0, R := 0, Q := 0) {
|
|
static M1 := "{", M2 := "}", S1 := "[", S2 := "]", N := "`n", C := ",", S := "- ", E := "", K := ":"
|
|
if (OT := Type(O)) = "Array" {
|
|
D := !R ? S1 : ""
|
|
for key, value in O {
|
|
F := (VT := Type(value)) = "Array" ? "S" : InStr("Map,Object", VT) ? "M" : E
|
|
Z := VT = "Array" && value.Length = 0 ? "[]" : ((VT = "Map" && value.count = 0) || (VT = "Object" && ObjOwnPropCount(value) = 0)) ? "{}" : ""
|
|
D .= (J > R ? "`n" CL(R + 2) : "") (F ? (%F%1 (Z ? "" : CO(value, J, R + 1, F)) %F%2) : ES(value)) (OT = "Array" && O.Length = A_Index ? E : C)
|
|
}
|
|
} else {
|
|
D := !R ? M1 : ""
|
|
for key, value in (OT := Type(O)) = "Map" ? (Y := 1, O) : (Y := 0, O.OwnProps()) {
|
|
F := (VT := Type(value)) = "Array" ? "S" : InStr("Map,Object", VT) ? "M" : E
|
|
Z := VT = "Array" && value.Length = 0 ? "[]" : ((VT = "Map" && value.count = 0) || (VT = "Object" && ObjOwnPropCount(value) = 0)) ? "{}" : ""
|
|
D .= (J > R ? "`n" CL(R + 2) : "") (Q = "S" && A_Index = 1 ? M1 : E) ES(key) K (F ? (%F%1 (Z ? "" : CO(value, J, R + 1, F)) %F%2) : ES(value)) (Q = "S" && A_Index = (Y ? O.count : ObjOwnPropCount(O)) ? M2 : E) (J != 0 || R ? (A_Index = (Y ? O.count : ObjOwnPropCount(O)) ? E : C) : E)
|
|
if J = 0 && !R
|
|
D .= (A_Index < (Y ? O.count : ObjOwnPropCount(O)) ? C : E)
|
|
}
|
|
}
|
|
if J > R
|
|
D .= "`n" CL(R + 1)
|
|
if R = 0
|
|
D := RegExReplace(D, "^\R+") (OT = "Array" ? S2 : M2)
|
|
return D
|
|
}
|
|
ES(S) {
|
|
switch Type(S) {
|
|
case "Float":
|
|
if (v := '', d := InStr(S, 'e'))
|
|
v := SubStr(S, d), S := SubStr(S, 1, d - 1)
|
|
if ((StrLen(S) > 17) && (d := RegExMatch(S, "(99999+|00000+)\d{0,3}$")))
|
|
S := Round(S, Max(1, d - InStr(S, ".") - 1))
|
|
return S v
|
|
case "Integer":
|
|
return S
|
|
case "String":
|
|
S := StrReplace(S, "\", "\\")
|
|
S := StrReplace(S, "`t", "\t")
|
|
S := StrReplace(S, "`r", "\r")
|
|
S := StrReplace(S, "`n", "\n")
|
|
S := StrReplace(S, "`b", "\b")
|
|
S := StrReplace(S, "`f", "\f")
|
|
S := StrReplace(S, "`v", "\v")
|
|
S := StrReplace(S, '"', '\"')
|
|
return '"' S '"'
|
|
default:
|
|
return S == JSON.true ? "true" : S == JSON.false ? "false" : "null"
|
|
}
|
|
}
|
|
CL(i) {
|
|
Loop (s := "", space ? i - 1 : 0)
|
|
s .= space
|
|
return s
|
|
}
|
|
}
|
|
} |