Created
January 10, 2018 23:06
-
-
Save knzm/59bd20bdf7b8906890cb64b3b23f1740 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ go run json_ordered2.go | |
==== map[string]interface{} ==== | |
Unmarshal: &map[string]interface {}{"bbb":10, "abc":20, "aaa":30} | |
{"aaa":30,"abc":20,"bbb":10} | |
==== FieldMap ==== | |
Unmarshal: &main.FieldMap{"bbb":main.Field{Order:0x1, Value:10}, "abc":main.Field{Order:0x2, Value:20}, "aaa":main.Field{Order:0x3, Value:30}} | |
{"bbb":10,"abc":20,"aaa":30} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"fmt" | |
"log" | |
"reflect" | |
"sort" | |
"sync" | |
) | |
func makeFieldName(name string, index int) string { | |
return fmt.Sprintf(`Field_%d`, index) | |
} | |
var mu sync.Mutex | |
var counter uint64 = 0 | |
type Field struct { | |
Order uint64 | |
Value interface{} | |
} | |
func (f *Field) UnmarshalJSON(data []byte) error { | |
counter++ | |
f.Order = counter | |
return json.Unmarshal(data, &f.Value) | |
} | |
type FieldMap map[string]Field | |
func (fmap *FieldMap) UnmarshalJSON(data []byte) error { | |
mu.Lock() | |
defer mu.Unlock() | |
counter = 0 | |
m := (*map[string]Field)(fmap) | |
return json.Unmarshal(data, m) | |
} | |
func (fmap FieldMap) MarshalJSON() ([]byte, error) { | |
var names []string | |
for name := range fmap { | |
names = append(names, name) | |
} | |
sort.Slice(names, func(i, j int) bool { | |
return fmap[names[i]].Order < fmap[names[j]].Order | |
}) | |
var fields []reflect.StructField | |
for i, name := range names { | |
field := reflect.StructField{ | |
Name: makeFieldName(name, i), | |
Type: reflect.TypeOf(fmap[name].Value), | |
Tag: reflect.StructTag(fmt.Sprintf(`json:"%s"`, name)), | |
} | |
fields = append(fields, field) | |
} | |
t := reflect.StructOf(fields) | |
v := reflect.New(t) | |
for i, name := range names { | |
fv := v.Elem().Field(i) | |
fv.Set(reflect.ValueOf(fmap[name].Value)) | |
} | |
return json.Marshal(v.Interface()) | |
} | |
var content = []byte(`{ | |
"bbb": 1, | |
"abc": 2, | |
"aaa": 3 | |
}`) | |
func Run() error { | |
testData := []struct { | |
name string | |
createfunc func() interface{} | |
}{ | |
{ | |
name: "map[string]interface{}", | |
createfunc: func() interface{} { | |
var m map[string]interface{} | |
return &m | |
}, | |
}, | |
{ | |
name: "FieldMap", | |
createfunc: func() interface{} { | |
return &FieldMap{} | |
}, | |
}, | |
} | |
for _, tt := range testData { | |
log.Printf("==== %s ====", tt.name) | |
m := tt.createfunc() | |
err := json.Unmarshal(content, m) | |
if err != nil { | |
return err | |
} | |
log.Printf("Unmarshal: %#v", m) | |
b, err := json.Marshal(m) | |
if err != nil { | |
return err | |
} | |
log.Printf("%s", b) | |
} | |
return nil | |
} | |
func main() { | |
if err := Run(); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment