-
Notifications
You must be signed in to change notification settings - Fork 72
/
Copy pathclass_JSON.ahk
144 lines (141 loc) · 7.31 KB
/
class_JSON.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/************************************************************************
* @description: JSON格式字符串序列化和反序列化, 修改自[HotKeyIt/Yaml](https://github.com/HotKeyIt/Yaml)
* 增加了对true/false/null类型的支持, 保留了数值的类型
* @author thqby, HotKeyIt
* @date 2021/10/04
* @version 1.0.1
***********************************************************************/
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 / ''
*/
static parse(text, keepbooltype := false) {
keepbooltype ? (_true := JSON.true, _false := JSON.false, _null := JSON.null) : (_true := true, _false := false, _null := "")
NQ := "", LF := "", LP := 0, P := "", R := ""
D := [C := (A := InStr(text := LTrim(text, " `t`r`n"), "[") = 1) ? [] : Map()], 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 = "[" ? [] : Map(), A ? D[L].Push(C) : 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
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
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, 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", Ord("e"), Chr(0x1B), Ord("N"), Chr(0x85), Ord("P"), Chr(0x2029), 0, "", Ord("L"), Chr(0x2028), Ord("_"), Chr(0xA0))
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, "^[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, "^[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.
* @param unicode_escaped Convert non-ascii characters to \uxxxx where unicode_escaped = true
*/
static stringify(obj, expandlevel := unset, space := " ", unicode_escaped := false) {
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, J, unicode_escaped)) (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, J, unicode_escaped) K (F ? (%F%1 (Z ? "" : CO(value, J, R + 1, F)) %F%2) : ES(value, J, unicode_escaped)) (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, J := 1, U := false) {
static ascii := Map("\", "\", "`a", "a", "`b", "b", "`t", "t", "`n", "n", "`v", "v", "`f", "f", "`r", "r", Chr(0x1B), "e", "`"", "`"", Chr(0x85), "N", Chr(0x2029), "P", Chr(0x2028), "L", "", "0", Chr(0xA0), "_")
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":
v := ""
if (U && RegExMatch(S, "m)[\x{7F}-\x{7FFF}]")) {
Loop Parse S
v .= ascii.Has(A_LoopField) ? "\" ascii[A_LoopField] : Ord(A_LoopField) < 128 ? A_LoopField : "\u" format("{1:.4X}", Ord(A_LoopField))
return '"' v '"'
} else {
Loop Parse S
v .= ascii.Has(A_LoopField) ? "\" ascii[A_LoopField] : A_LoopField
return '"' v '"'
}
default:
return S == JSON.true ? "true" : S == JSON.false ? "false" : "null"
}
}
CL(i) {
Loop (s := "", i - 1)
s .= space
return s
}
}
}