-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodifier.go
147 lines (131 loc) · 2.87 KB
/
modifier.go
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
145
146
147
package jsontools
import (
"errors"
"unicode/utf8"
)
type JsonModifier struct {
limit int
inplace bool
filterKeySet map[string]struct{}
}
type JsonModifierOption func(*JsonModifier)
func WithFieldLengthLimit(limit int) JsonModifierOption {
return func(m *JsonModifier) {
m.limit = limit
}
}
func WithInplace(inplace bool) JsonModifierOption {
return func(m *JsonModifier) {
m.inplace = inplace
}
}
func WithFilterKeys(keys ...string) JsonModifierOption {
return func(m *JsonModifier) {
m.filterKeySet = make(map[string]struct{}, len(keys))
for _, key := range keys {
k := `"` + key + `"`
m.filterKeySet[k] = struct{}{}
}
}
}
func NewJsonModifier(opts ...JsonModifierOption) *JsonModifier {
m := &JsonModifier{}
for _, opt := range opts {
opt(m)
}
return m
}
func (m *JsonModifier) ModifyJson(data []byte) ([]byte, error) {
if len(data) == 0 {
return data, nil
}
var dst []byte
if m.inplace {
dst = data[:0]
} else {
dst = make([]byte, 0, len(data))
}
skipComma := false
expectStackSize := 0
parser := NewJsonParser(data, func(ctx HandlerContext) error {
// filter keys ------- begin -------
if expectStackSize > 0 {
if ctx.StackSize >= expectStackSize {
// skip all colon, comma and values of this key
return nil
} else {
expectStackSize = 0
skipComma = true
}
}
switch ctx.Kind {
case KindObjectKey:
// object key must be string, therefore, this if could be removed
if _, ok := m.filterKeySet[string(ctx.Value)]; ok {
// skip this key
// m.skipColon = true
expectStackSize = ctx.StackSize
return nil
}
case KindOther:
if skipComma {
skipComma = false
if ctx.Token == SepComma {
// skip this comma
return nil
}
}
}
// filter keys ------- end -------
// modify value ------- begin -------
needModify := false
if m.limit > 0 {
switch ctx.Kind {
case KindObjectValue,
KindArrayValue:
if ctx.Token == String && utf8.RuneCount(ctx.Value) > m.limit+2 {
needModify = true
}
}
}
if needModify {
dst = append(dst, '"')
count := 0
slashCount := 0
for i := 1; ; {
r, size := utf8.DecodeRune(ctx.Value[i:])
if r == utf8.RuneError {
return errors.New("invalid utf8")
}
if r == '\\' {
slashCount++
} else {
slashCount = 0
}
dst = append(dst, ctx.Value[i:i+size]...)
i += size
count++
if count >= m.limit {
break
}
}
if slashCount > 0 && slashCount%2 == 1 {
dst = dst[:len(dst)-1] // remove the last slash
}
dst = append(dst, '"')
} else {
dst = append(dst, ctx.Value...)
}
// modify value ------- end -------
return nil
})
err := parser.Parse()
if err != nil {
return nil, err
}
return dst, nil
}
func ModifyJson(data []byte, opts ...JsonModifierOption) ([]byte, error) {
m := NewJsonModifier(opts...)
return m.ModifyJson(data)
}