-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathflattener.go
More file actions
124 lines (111 loc) · 2.74 KB
/
Copy pathflattener.go
File metadata and controls
124 lines (111 loc) · 2.74 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
package flatjson
import (
"bytes"
"encoding/json"
"fmt"
"io"
"regexp"
"sort"
"strconv"
)
var (
unquotedPropertyNameRegexp = regexp.MustCompile(`^[A-Za-z_][0-9A-Za-z_]*$`)
keywords = map[string]bool{
"false": true,
"null": true,
"true": true,
}
)
// A Flattener converts JSON into flat JSON.
type Flattener struct {
w io.Writer
prefix string
suffix string
}
// A FlattenerOption sets an option on a Flattener.
type FlattenerOption func(*Flattener)
func propertyAccessor(path, name string) string {
if unquotedPropertyNameRegexp.MatchString(name) && !keywords[name] {
if path == "" {
return name
}
return path + "." + name
}
return fmt.Sprintf("%s[%q]", path, name)
}
// NewFlattener returns a new Flattener that writes to w.
func NewFlattener(w io.Writer, options ...FlattenerOption) *Flattener {
f := &Flattener{
w: w,
prefix: "root",
suffix: ";\n",
}
for _, option := range options {
option(f)
}
return f
}
func (f *Flattener) writeArrayValues(path string, array []interface{}) error {
if _, err := fmt.Fprintf(f.w, "%s = []%s", path, f.suffix); err != nil {
return err
}
for i, value := range array {
if err := f.writeValuesHelper(path+"["+strconv.Itoa(i)+"]", value); err != nil {
return err
}
}
return nil
}
func (f *Flattener) writeObjectValues(path string, object map[string]interface{}) error {
if _, err := fmt.Fprintf(f.w, "%s = {}%s", path, f.suffix); err != nil {
return err
}
properties := make([]string, 0, len(object))
for property := range object {
properties = append(properties, property)
}
sort.Strings(properties)
for _, property := range properties {
if err := f.writeValuesHelper(propertyAccessor(path, property), object[property]); err != nil {
return err
}
}
return nil
}
func (f *Flattener) writeValuesHelper(path string, value interface{}) error {
switch value := value.(type) {
case []interface{}:
return f.writeArrayValues(path, value)
case map[string]interface{}:
return f.writeObjectValues(path, value)
default:
data, err := json.Marshal(value)
if err != nil {
return err
}
_, err = f.w.Write([]byte(path + " = " + string(data) + f.suffix))
return err
}
}
// WriteValues decodes JSON from data and writes it.
func (f *Flattener) WriteValues(data []byte) error {
d := json.NewDecoder(bytes.NewReader(data))
d.UseNumber()
var value interface{}
if err := d.Decode(&value); err != nil {
return err
}
return f.writeValuesHelper(f.prefix, value)
}
// WithPrefix sets the prefix on a Flattener.
func WithPrefix(prefix string) FlattenerOption {
return func(f *Flattener) {
f.prefix = prefix
}
}
// WithSuffix sets the suffix on a Flattener.
func WithSuffix(suffix string) FlattenerOption {
return func(f *Flattener) {
f.suffix = suffix
}
}