-
Notifications
You must be signed in to change notification settings - Fork 30
/
flatten.go
101 lines (87 loc) · 2.48 KB
/
flatten.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
package json2csv
import (
"encoding/json"
"fmt"
"reflect"
"sort"
"strconv"
"github.com/yukithm/json2csv/jsonpointer"
)
var jsonNumberType = reflect.TypeOf(json.Number(""))
type mapKeys []reflect.Value
func (k mapKeys) Len() int { return len(k) }
func (k mapKeys) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
func (k mapKeys) Less(i, j int) bool { return k[i].String() < k[j].String() }
func sortedMapKeys(v reflect.Value) []reflect.Value {
var keys mapKeys = v.MapKeys()
sort.Sort(keys)
return keys
}
// KeyValue represents key(path)/value map.
type KeyValue map[string]interface{}
// Keys returns all keys.
func (kv KeyValue) Keys() []string {
keys := make([]string, 0, len(kv))
for k := range kv {
keys = append(keys, k)
}
return keys
}
func flatten(obj interface{}) (KeyValue, error) {
f := make(KeyValue, 0)
key := jsonpointer.JSONPointer{}
if err := _flatten(f, obj, key); err != nil {
return nil, err
}
return f, nil
}
func _flatten(out KeyValue, obj interface{}, key jsonpointer.JSONPointer) error {
value, ok := obj.(reflect.Value)
if !ok {
value = reflect.ValueOf(obj)
}
for value.Kind() == reflect.Interface {
value = value.Elem()
}
if value.IsValid() {
vt := value.Type()
if vt.AssignableTo(jsonNumberType) {
out[key.String()] = value.Interface().(json.Number)
return nil
}
}
switch value.Kind() {
case reflect.Map:
_flattenMap(out, value, key)
case reflect.Slice:
_flattenSlice(out, value, key)
case reflect.String:
out[key.String()] = value.String()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
out[key.String()] = value.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
out[key.String()] = value.Uint()
case reflect.Float32, reflect.Float64:
out[key.String()] = value.Float()
case reflect.Bool:
out[key.String()] = value.Bool()
default:
return fmt.Errorf("Unknown kind: %s", value.Kind())
}
return nil
}
func _flattenMap(out map[string]interface{}, value reflect.Value, prefix jsonpointer.JSONPointer) {
keys := sortedMapKeys(value)
for _, key := range keys {
pointer := prefix.Clone()
pointer.AppendString(key.String())
_flatten(out, value.MapIndex(key).Interface(), pointer)
}
}
func _flattenSlice(out map[string]interface{}, value reflect.Value, prefix jsonpointer.JSONPointer) {
for i := 0; i < value.Len(); i++ {
pointer := prefix.Clone()
pointer.AppendString(strconv.Itoa(i))
_flatten(out, value.Index(i).Interface(), pointer)
}
}