by October 9, 2019
onIn Golang you sometimes want to modify or extend the (JSON) serialization of a type that you cannot modify itself (e.g. part of an external library) or maybe you want to serialize the same type in different kinds.
Usually however you define the type of serialization via type tags as part of the struct declaration.
You can get around this fact by facilitating an anonymous type and embed your target type as part of it. A quick example might look like this:
// Record is the 'fixed' type struct that we cannot or
// don't want to modify for some reason (e.g. external library)
type Record struct {
Name string `json:"name"`
Value int `json:"value"`
// this is the default type definition
// we want to override later on
Created time.Time `json:"created"`
}
func customJSON(record *Record) string {
type Alias Record
return toJSON(&struct {
*Alias
// here we change to serialization of the 'created'
// field to serialize into 'epoch milliseconds'
CustomCreated int64 `json:"created"`
}{
Alias: (*Alias)(record),
CustomCreated: record.Created.Unix() * 1000,
})
}
For demonstration sake the example above might look like the following:
// test.go
package main
import (
"encoding/json"
"fmt"
"time"
)
// ...
func toJSON(value interface{}) string {
bytes, err := json.Marshal(value)
if err != nil {
panic("failed to serialize to JSON")
}
return string(bytes)
}
func main() {
record := &Record{"foo", 100, time.Now()}
fmt.Printf("Record (original): %v\n", toJSON(record))
fmt.Printf("Record (custom): %v\n", customJSON(record))
}
$ go run test.go
Record (original): {"name":"foo","value":100,"created":"2019-10-09T21:58:37.219719789+02:00"}
Record (custom): {"name":"foo","value":100,"created":1570651117000}
The shown practice is not specific to JSON serialization at all - it can be used for other tag based values as well of course.