Skip to content

Instantly share code, notes, and snippets.

@austindoeswork
Created January 10, 2017 01:57
Show Gist options
  • Save austindoeswork/5af86ae6d13de6c228cd6c09128d49f6 to your computer and use it in GitHub Desktop.
Save austindoeswork/5af86ae6d13de6c228cd6c09128d49f6 to your computer and use it in GitHub Desktop.
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