-
Notifications
You must be signed in to change notification settings - Fork 0
/
flat.go
141 lines (131 loc) · 4.02 KB
/
flat.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
// Package goflat provides functions for flattening and unflattening JSON objects.
// It supports flattening JSON arrays as well as nested maps.
package goflat
import (
"encoding/json"
"fmt"
"strings"
)
// Options represents the options for flattening and unflattening JSON.
type Options struct {
KeyDelimiter string // The delimiter to use for separating keys in the flattened structure
MaxDepth int // The maximum depth for flattening
}
// DefaultOptions returns the default options for flattening and unflattening JSON.
func DefaultOptions() Options {
return Options{
KeyDelimiter: ".",
MaxDepth: -1, // -1 indicates no maximum depth
}
}
// FlattenJSON flattens a JSON object into a map[string]interface{} using the specified options.
// It supports flattening JSON arrays as well.
//
// Example:
//
// data := []byte(`{"name": "John", "age": 30, "address": {"city": "New York", "state": "NY"}}`)
// options := DefaultOptions()
// flattened, err := FlattenJSON(data, options)
// if err != nil {
// fmt.Println("Error:", err)
// return
// }
// fmt.Println(flattened)
//
// Output:
//
// map[address.city:New York address.state:NY age:30 name:John]
func FlattenJSON(data []byte, options Options) (map[string]interface{}, error) {
var result map[string]interface{}
err := json.Unmarshal(data, &result)
if err != nil {
return nil, err
}
flattened := make(map[string]interface{})
flatten("", result, flattened, options.KeyDelimiter, options.MaxDepth)
return flattened, nil
}
// FlattenMap flattens a map[string]interface{} into a map[string]interface{} using the specified options.
// It supports flattening nested maps as well.
//
// Example:
//
// data := map[string]interface{}{
// "name": "John",
// "age": 30,
// "address": map[string]interface{}{
// "city": "New York",
// "state": "NY",
// },
// }
// options := DefaultOptions()
// flattened := FlattenMap(data, options)
// fmt.Println(flattened)
//
// Output:
//
// map[address.city:New York address.state:NY age:30 name:John]
func FlattenMap(data map[string]interface{}, options Options) map[string]interface{} {
flattened := make(map[string]interface{})
flatten("", data, flattened, options.KeyDelimiter, options.MaxDepth)
return flattened
}
// flatten is a helper function that recursively flattens a JSON object.
func flatten(prefix string, value interface{}, flattened map[string]interface{}, delimiter string, maxDepth int) {
if maxDepth == 0 {
flattened[strings.TrimSuffix(prefix, delimiter)] = value
return
}
switch v := value.(type) {
case map[string]interface{}:
for key, val := range v {
flatten(prefix+key+delimiter, val, flattened, delimiter, maxDepth-1)
}
case []interface{}:
for i, val := range v {
flatten(prefix+fmt.Sprintf("[%d]"+delimiter, i), val, flattened, delimiter, maxDepth-1)
}
default:
flattened[strings.TrimSuffix(prefix, delimiter)] = v
}
}
// UnflattenJSON unflattens a flattened JSON object into its original structure.
//
// Example:
//
// flattened := map[string]interface{}{
// "address.city": "New York",
// "address.state": "NY",
// "age": 30,
// "name": "John",
// }
// options := DefaultOptions()
// unflattened, err := UnflattenJSON(flattened, options)
// if err != nil {
// fmt.Println("Error:", err)
// return
// }
// fmt.Println(unflattened)
//
// Output:
//
// map[address:map[city:New York state:NY] age:30 name:John]
func UnflattenJSON(flattened map[string]interface{}, options Options) (interface{}, error) {
result := make(map[string]interface{})
for key, value := range flattened {
setValue(result, strings.Split(key, options.KeyDelimiter), value)
}
return result, nil
}
// setValue is a helper function that sets a value in a nested map based on the given key path.
func setValue(data map[string]interface{}, keys []string, value interface{}) {
lastKey := keys[len(keys)-1]
parent := data
for _, key := range keys[:len(keys)-1] {
if _, ok := parent[key]; !ok {
parent[key] = make(map[string]interface{})
}
parent = parent[key].(map[string]interface{})
}
parent[lastKey] = value
}