Skip to content

Instantly share code, notes, and snippets.

@knzm
Created January 10, 2018 23:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save knzm/59bd20bdf7b8906890cb64b3b23f1740 to your computer and use it in GitHub Desktop.
Save knzm/59bd20bdf7b8906890cb64b3b23f1740 to your computer and use it in GitHub Desktop.
$ 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}
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