-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathisjson.go
More file actions
136 lines (133 loc) · 3.79 KB
/
isjson.go
File metadata and controls
136 lines (133 loc) · 3.79 KB
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
package isjson
func IsJSON(data []byte) bool {
if data == nil || len(data) == 0 {
return false
}
// The first byte must either include an opening bracket or whitespace.
switch data[0] {
case '{', '[', ' ', '\t', '\n', '\r':
default:
return false
}
s := 0
e := len(data) - 1
ss := func() {
for s < e {
switch data[s] {
case ' ', '\t', '\n', '\r':
default:
return
}
s++
}
}
// Skip leading whitespace.
ss()
se := func() {
for e > s {
switch data[e] {
case ' ', '\t', '\n', '\r':
default:
return
}
e--
}
}
// Skip trailing whitespace.
se()
// If the pointers crossed then we found nothing but whitespace.
if s == e {
return false
}
// A JSON string must start with either '{' or '['.
switch data[s] {
case '{':
// '{' needs a closing '}'.
if data[e] != '}' {
return false
}
case '[':
// '[' needs a closing ']'.
if data[e] != ']' {
return false
}
default:
return false
}
// It's either '{}' or '[]'.
if s == e - 1 {
return true
}
// Remember the opening bracket.
bkt := data[s]
// Move past the brackets.
s++
e--
// Skip any whitespace until the next token.
ss()
// Evaluate the token using last seen bracket for additional context.
// We can use the following rules:
// '{' token can only be followed by a nested object or a quoted field name
// '[' token can be followed by a value, i.e. an object, a number, a quoted
// string, or a true/false/null literal.
switch bkt {
case '{':
switch data[s] {
case '{': // nested object
return IsJSON(data[s:e+1])
case '"', '\'': // quoted field name
q := data[s]
s++
// Search for a closing quotation mark.
for s < e {
switch data[s] {
case '"', '\'':
// Ignore escaped quotation marks. (necessary?)
if data[s-1] == '\\' {
break
}
// The quotation mark char doesn't match the opening.
if data[s] != q {
break
}
// Move past the quotation mark and skip whitespace until
// the next token.
s++
ss()
// Next character must be a colon or it's not a valid JSON.
if data[s] == ':' {
return true
}
return false
}
s++
}
// Closing quotation mark not found.
return false
default:
return false
}
case '[':
// Check if it is a valid value.
switch {
case data[s] == '{' || data[s] == '[': // object
return IsJSON(data[s:e+1])
case data[s] == '"' || data[s] == '\'': // quoted string
return true
return true
case data[s] >= '0' && data[s] <= '9': // number
return true
case e - s >= 1 && data[s] == '-' && data[s+1] >= '0' && data[s+1] <= '9': // negative number
return true
case e - s >= 3 && string(data[s:s+4]) == "true":
return true
case e - s >= 3 && string(data[s:s+4]) == "null":
return true
case e - s >= 4 && string(data[s:s+5]) == "false":
return true
default:
return false
}
}
return false
}