Created
January 10, 2017 01:57
-
-
Save austindoeswork/5af86ae6d13de6c228cd6c09128d49f6 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
package main | |
import ( | |
"bytes" | |
"encoding/json" | |
"fmt" | |
"reflect" | |
"sort" | |
"strconv" | |
"time" | |
) | |
type JSONTime struct { | |
time.Time | |
} | |
func (t JSONTime) MarshalJSON() ([]byte, error) { | |
return serialize(t.UTC()), nil | |
} | |
func main() { | |
t := JSONTime{time.Now().UTC()} | |
b, err := json.Marshal(t) | |
if err != nil { | |
fmt.Println(err) | |
} | |
fmt.Printf("%s\n", b) | |
} | |
////////// | |
type structFieldFilter func(reflect.StructField) (string, bool) | |
func formatValue(val reflect.Value, fltr structFieldFilter) string { | |
if val.Kind() == reflect.String { | |
return "\"" + val.Interface().(string) + "\"" | |
} | |
var buf bytes.Buffer | |
writeValue(&buf, val, fltr) | |
return string(buf.Bytes()) | |
} | |
func filterField(f reflect.StructField) (string, bool) { | |
name := f.Name | |
if str := f.Tag.Get("hash"); str != "" { | |
if str == "-" { | |
return "", false | |
} | |
} | |
return name, true | |
} | |
func serialize(object interface{}) []byte { | |
var buf bytes.Buffer | |
writeValue(&buf, reflect.ValueOf(object), | |
func(f reflect.StructField) (string, bool) { | |
return filterField(f) | |
}) | |
fmt.Println(buf.String()) | |
return buf.Bytes() | |
} | |
func writeValue(buf *bytes.Buffer, val reflect.Value, fltr structFieldFilter) { | |
switch val.Kind() { | |
case reflect.String: | |
buf.WriteByte('"') | |
buf.WriteString(val.String()) | |
buf.WriteByte('"') | |
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
buf.WriteString(strconv.FormatInt(val.Int(), 10)) | |
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |
buf.WriteString(strconv.FormatUint(val.Uint(), 10)) | |
case reflect.Bool: | |
if val.Bool() { | |
buf.WriteByte('t') | |
} else { | |
buf.WriteByte('f') | |
} | |
case reflect.Ptr: | |
if !val.IsNil() || val.Type().Elem().Kind() == reflect.Struct { | |
writeValue(buf, reflect.Indirect(val), fltr) | |
} else { | |
writeValue(buf, reflect.Zero(val.Type().Elem()), fltr) | |
} | |
case reflect.Array, reflect.Slice: | |
buf.WriteByte('[') | |
len := val.Len() | |
for i := 0; i < len; i++ { | |
if i != 0 { | |
buf.WriteByte(',') | |
} | |
writeValue(buf, val.Index(i), fltr) | |
} | |
buf.WriteByte(']') | |
case reflect.Map: | |
mk := val.MapKeys() | |
items := make([]item, len(mk), len(mk)) | |
// Get all values | |
for i, _ := range items { | |
items[i].name = formatValue(mk[i], fltr) | |
items[i].value = val.MapIndex(mk[i]) | |
} | |
// Sort values by key | |
sort.Sort(itemSorter(items)) | |
buf.WriteByte('[') | |
for i, _ := range items { | |
if i != 0 { | |
buf.WriteByte(',') | |
} | |
buf.WriteString(items[i].name) | |
buf.WriteByte(':') | |
writeValue(buf, items[i].value, fltr) | |
} | |
buf.WriteByte(']') | |
case reflect.Struct: | |
vtype := val.Type() | |
flen := vtype.NumField() | |
items := make([]item, 0, flen) | |
// Get all fields | |
for i := 0; i < flen; i++ { | |
field := vtype.Field(i) | |
it := item{field.Name, val.Field(i)} | |
if fltr != nil { | |
if name, ok := fltr(field); ok { | |
it.name = name | |
} else { | |
continue | |
} | |
} | |
items = append(items, it) | |
} | |
// Sort fields by name | |
sort.Sort(itemSorter(items)) | |
buf.WriteByte('{') | |
for i, _ := range items { | |
if i != 0 { | |
buf.WriteByte(',') | |
} | |
buf.WriteString(items[i].name) | |
buf.WriteByte(':') | |
writeValue(buf, items[i].value, fltr) | |
} | |
buf.WriteByte('}') | |
default: | |
buf.WriteString(val.String()) | |
} | |
} | |
type item struct { | |
name string | |
value reflect.Value | |
} | |
type itemSorter []item | |
func (s itemSorter) Len() int { | |
return len(s) | |
} | |
func (s itemSorter) Swap(i, j int) { | |
s[i], s[j] = s[j], s[i] | |
} | |
func (s itemSorter) Less(i, j int) bool { | |
return s[i].name < s[j].name | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment