@@ -10,6 +10,15 @@ import (
10
10
"github.com/hashicorp/go-version"
11
11
)
12
12
13
+ // A KVStore is a simple key-value store.
14
+ // The default implementation uses a map[string]interface{}.
15
+ // This is fast, however will lead to inconsistent ordering of the keys in the generated JSON.
16
+ // A custom implementation can be used to maintain the order of the keys.
17
+ type KVStore interface {
18
+ Set (k string , v interface {})
19
+ Each (f func (k string , v interface {}))
20
+ }
21
+
13
22
// A FieldFilter is a function that decides whether a field should be marshalled or not.
14
23
// If it returns true, the field will be marshalled, otherwise it will be skipped.
15
24
type FieldFilter func (field reflect.StructField ) (bool , error )
@@ -38,6 +47,12 @@ type Options struct {
38
47
// This option is false by default.
39
48
IncludeEmptyTag bool
40
49
50
+ // The KVStoreFactory is a function that returns a new KVStore.
51
+ // The default implementation uses a map[string]interface{}, which is fast but does not maintain the order of the
52
+ // keys.
53
+ // A custom implementation can be used to maintain the order of the keys, i.e. using github.com/wk8/go-ordered-map
54
+ KVStoreFactory func () KVStore
55
+
41
56
// This is used internally so that we can propagate anonymous fields groups tag to all child field.
42
57
nestedGroupsMap map [string ][]string
43
58
}
@@ -81,6 +96,12 @@ func Marshal(options *Options, data interface{}) (interface{}, error) {
81
96
options .FieldFilter = createDefaultFieldFilter (options )
82
97
}
83
98
99
+ if options .KVStoreFactory == nil {
100
+ options .KVStoreFactory = func () KVStore {
101
+ return kvStore {}
102
+ }
103
+ }
104
+
84
105
if t .Kind () == reflect .Ptr {
85
106
// follow pointer
86
107
t = t .Elem ()
@@ -94,7 +115,7 @@ func Marshal(options *Options, data interface{}) (interface{}, error) {
94
115
return marshalValue (options , v )
95
116
}
96
117
97
- dest := make ( map [ string ] interface {} )
118
+ dest := options . KVStoreFactory ( )
98
119
99
120
for i := 0 ; i < t .NumField (); i ++ {
100
121
field := t .Field (i )
@@ -172,13 +193,13 @@ func Marshal(options *Options, data interface{}) (interface{}, error) {
172
193
173
194
// when a composition field we want to bring the child
174
195
// nodes to the top
175
- nestedVal , ok := v .(map [ string ] interface {} )
196
+ nestedVal , ok := v .(KVStore )
176
197
if isEmbeddedField && ok {
177
- for key , value := range nestedVal {
178
- dest [ key ] = value
179
- }
198
+ nestedVal . Each ( func ( k string , v interface {}) {
199
+ dest . Set ( k , v )
200
+ })
180
201
} else {
181
- dest [ jsonTag ] = v
202
+ dest . Set ( jsonTag , v )
182
203
}
183
204
}
184
205
@@ -303,13 +324,14 @@ func marshalValue(options *Options, v reflect.Value) (interface{}, error) {
303
324
if mapKeys [0 ].Kind () != reflect .String {
304
325
return nil , MarshalInvalidTypeError {t : mapKeys [0 ].Kind (), data : val }
305
326
}
306
- dest := make (map [string ]interface {})
327
+
328
+ dest := options .KVStoreFactory ()
307
329
for _ , key := range mapKeys {
308
330
d , err := marshalValue (options , v .MapIndex (key ))
309
331
if err != nil {
310
332
return nil , err
311
333
}
312
- dest [ key .String ()] = d
334
+ dest . Set ( key .String (), d )
313
335
}
314
336
return dest , nil
315
337
}
0 commit comments