-
Notifications
You must be signed in to change notification settings - Fork 0
/
json.go
104 lines (95 loc) · 2.95 KB
/
json.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
package main
import (
"fmt"
"strings"
"github.com/tidwall/gjson"
)
type JSONProcessor struct {
jsonData []byte
keys []string
}
// JSONProcessor extract keys from JSON data
// seenKeys is used to prevent duplicate keys
// walk is a recursive function to walk through JSON data
func (jp *JSONProcessor) extractKeys() {
seenKeys := make(map[string]struct{})
var walk func(prefix string, value gjson.Result)
walk = func(prefix string, value gjson.Result) {
if value.IsObject() {
jp.processObject(prefix, value, seenKeys, walk)
} else if value.IsArray() {
jp.processArray(prefix, value, seenKeys, walk)
}
}
walk("", gjson.ParseBytes(jp.jsonData))
// remove invalid keys
jp.keys = filterInvalidKeys(jp.keys)
}
// processObject processes JSON objects and extracts keys
func (jp *JSONProcessor) processObject(prefix string, value gjson.Result, seenKeys map[string]struct{}, walk func(string, gjson.Result)) {
value.ForEach(func(key, val gjson.Result) bool {
fullKey := key.String()
if prefix != "" {
fullKey = prefix + "." + fullKey
}
if _, exists := seenKeys[fullKey]; !exists {
seenKeys[fullKey] = struct{}{}
jp.keys = append(jp.keys, fullKey)
}
walk(fullKey, val)
return true
})
}
// processArray processes JSON arrays and extracts keys
func (jp *JSONProcessor) processArray(prefix string, value gjson.Result, seenKeys map[string]struct{}, walk func(string, gjson.Result)) {
arrayKey := fmt.Sprintf("%s.#", prefix)
if _, exists := seenKeys[arrayKey]; !exists {
seenKeys[arrayKey] = struct{}{}
jp.keys = append(jp.keys, arrayKey)
}
value.ForEach(func(index, val gjson.Result) bool {
elementKey := fmt.Sprintf("%s[%d]", prefix, index.Int())
if _, exists := seenKeys[elementKey]; !exists {
seenKeys[elementKey] = struct{}{}
jp.keys = append(jp.keys, elementKey)
}
walk(elementKey, val)
return true
})
// add array element keys (e.g. foo[].name)
if prefix != "" {
value.ForEach(func(_, val gjson.Result) bool {
val.ForEach(func(key, val gjson.Result) bool {
fullKey := fmt.Sprintf("%s[].%s", prefix, key.String())
fullKey = strings.TrimSuffix(fullKey, ".")
if _, exists := seenKeys[fullKey]; !exists {
seenKeys[fullKey] = struct{}{}
jp.keys = append(jp.keys, fullKey)
}
// add nested array element keys (e.g. foo[].bar[0])
if val.IsArray() {
val.ForEach(func(index, nestedVal gjson.Result) bool {
nestedKey := fmt.Sprintf("%s[%d]", fullKey, index.Int())
if _, exists := seenKeys[nestedKey]; !exists {
seenKeys[nestedKey] = struct{}{}
jp.keys = append(jp.keys, nestedKey)
}
return true
})
}
return true
})
return false // only process the first element
})
}
}
// remove invalid keys (e.g. "foo[]")
func filterInvalidKeys(keys []string) []string {
var validKeys []string
for _, key := range keys {
if !strings.HasSuffix(key, "[]") { // remove array with '[]' suffix
validKeys = append(validKeys, key)
}
}
return validKeys
}