Skip to content

Instantly share code, notes, and snippets.

@bojand
Last active May 16, 2018 23:53
Show Gist options
  • Save bojand/5535e81bfca5d9a89f1b4078f229e1ea to your computer and use it in GitHub Desktop.
Save bojand/5535e81bfca5d9a89f1b4078f229e1ea to your computer and use it in GitHub Desktop.
Custom JSON Marshal
package main
import (
"encoding/json"
"fmt"
"time"
)
const layout = "2006-01-02"
type MyType struct {
Count int `json:"count"`
Day time.Time `json:"day"`
Name string `json:"name"`
// ignored internal
Secret string `json:"-"`
}
func (m *MyType) UnmarshalJSON(data []byte) error {
type Alias MyType
aux := &struct {
Day string `json:"day"`
*Alias
}{
Alias: (*Alias)(m),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
m.Day, _ = time.Parse(layout, aux.Day)
return nil
}
func (m MyType) MarshalJSON() ([]byte, error) {
type Alias MyType
return json.Marshal(&struct {
Day string `json:"day"`
*Alias
}{
Day: m.Day.Format(layout),
Alias: (*Alias)(&m),
})
}
func main() {
var d MyType
jsonStr := `{"count":1111,"day":"2018-01-26","name":"foo"}`
_ = json.Unmarshal([]byte(jsonStr), &d)
fmt.Printf("%+v\n", d)
s, _ := time.Parse(layout, "2018-02-21")
d2 := MyType{Count: 1234, Day: s, Name: "bar", Secret: "asdf"}
fmt.Printf("%+v\n", d2)
dStr, _ := json.Marshal(d2)
fmt.Println(string(dStr))
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"time"
)
const (
milli1 = 1 * time.Millisecond
milli2 = 2 * time.Millisecond
milli3 = 3 * time.Millisecond
milli4 = 4 * time.Millisecond
milli5 = 5 * time.Millisecond
)
// Threshold represends a threshold limit we may care about
type Threshold int
const (
// ThresholdMean is the threshold for mean / average
ThresholdMean Threshold = iota
// ThresholdMedian is the threshold for the median
ThresholdMedian
// Threshold95th is the threshold for the 95th percentile
Threshold95th
// Threshold99th is the threshold for the 96th percentile
Threshold99th
)
// String() is the string representation of threshold
func (t Threshold) String() string {
tholds := [...]string{"mean", "median", "95th", "99th"}
if t < ThresholdMean || t > Threshold99th {
return ""
}
return tholds[t]
}
func ThresholdFromString(str string) Threshold {
str = strings.ToLower(str)
if str == "1" || str == "median" {
return ThresholdMedian
} else if str == "2" || str == "95th" {
return Threshold95th
} else if str == "3" || str == "99th" {
return Threshold99th
}
return ThresholdMean
}
// UnmarshalJSON prases a Threshold value from JSON string
func (t *Threshold) UnmarshalJSON(b []byte) error {
*t = ThresholdFromString(string(b))
return nil
}
// MarshalJSON formats a Threshold value into a JSON string
func (t Threshold) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("\"%s\"", t.String())), nil
}
// TestStatus represents a status of a test, whether its latest run failed the threshold settings
type TestStatus int
const (
// StatusOK means the latest run in test was within the threshold
StatusOK TestStatus = iota
// StatusFail means the latest run in test was not within the threshold
StatusFail
)
// String() is the string representation of threshold
func (t TestStatus) String() string {
if t == StatusFail {
return "fail"
}
return "ok"
}
func TestStatusFromString(str string) TestStatus {
str = strings.ToLower(str)
t := StatusOK
if str == "1" || str == "fail" {
t = StatusFail
}
return t
}
// UnmarshalJSON prases a Threshold value from JSON string
func (t *TestStatus) UnmarshalJSON(b []byte) error {
*t = TestStatusFromString(string(b))
return nil
}
// MarshalJSON formats a Threshold value into a JSON string
func (t TestStatus) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("\"%s\"", t.String())), nil
}
// Test represents a test
type Test struct {
Name string `json:"name"`
Status TestStatus `json:"status"`
Thresholds map[Threshold]*ThresholdSetting `json:"thresholds"`
FailOnError bool `json:"failOnError"`
}
func (m Test) MarshalJSON() ([]byte, error) {
type Alias Test
aux := &struct {
Thresholds map[string]*ThresholdSetting `json:"thresholds"`
*Alias
}{
Thresholds: make(map[string]*ThresholdSetting),
Alias: (*Alias)(&m),
}
for k, v := range m.Thresholds {
aux.Thresholds[k.String()] = v
}
return json.Marshal(aux)
}
func (m *Test) UnmarshalJSON(data []byte) error {
type Alias Test
aux := &struct {
Thresholds map[string]*ThresholdSetting `json:"thresholds"`
*Alias
}{
Alias: (*Alias)(m),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
m.Thresholds = make(map[Threshold]*ThresholdSetting)
for k, v := range aux.Thresholds {
kt := ThresholdFromString(k)
m.Thresholds[kt] = v
}
return nil
}
// ThresholdSetting setting
type ThresholdSetting struct {
Status TestStatus `json:"status"`
Threshold time.Duration `json:"threshold"`
}
func (m ThresholdSetting) MarshalJSON() ([]byte, error) {
type Alias ThresholdSetting
aux := &struct {
Status string `json:"status"`
*Alias
}{
Status: m.Status.String(),
Alias: (*Alias)(&m),
}
return json.Marshal(aux)
}
func (m *ThresholdSetting) UnmarshalJSON(data []byte) error {
type Alias ThresholdSetting
aux := &struct {
Status string `json:"status"`
*Alias
}{
Alias: (*Alias)(m),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
m.Status = TestStatusFromString(aux.Status)
return nil
}
func main() {
t := &Test{Name: "test1", Thresholds: map[Threshold]*ThresholdSetting{
ThresholdMean: &ThresholdSetting{Threshold: milli5},
ThresholdMedian: &ThresholdSetting{Threshold: milli5},
Threshold95th: &ThresholdSetting{Threshold: milli5},
Threshold99th: &ThresholdSetting{Threshold: milli5},
}, FailOnError: true}
str, err := json.Marshal(t)
if err != nil {
fmt.Println(err.Error())
}
fmt.Printf("%#v\n\n", string(str))
var out bytes.Buffer
json.Indent(&out, str, "", " ")
fmt.Printf("%s\n\n", string(out.Bytes()))
jsonStr := "{\"name\":\"\",\"status\":\"ok\",\"thresholds\":{\"0\":{\"status\":\"ok\",\"threshold\":5000000},\"1\":{\"status\":\"ok\",\"threshold\":5000000},\"2\":{\"status\":\"ok\",\"threshold\":5000000},\"3\":{\"status\":\"ok\",\"threshold\":5000000}},\"failOnError\":true}"
t2 := &Test{}
if err := json.Unmarshal([]byte(jsonStr), &t2); err != nil {
fmt.Println(err.Error())
}
fmt.Printf("%#v\n\n", t2)
jsonStr = "{\"thresholds\":{\"95th\":{\"status\":\"fail\",\"threshold\":5000000},\"99th\":{\"status\":\"ok\",\"threshold\":5000000},\"mean\":{\"status\":\"ok\",\"threshold\":5000000},\"median\":{\"status\":\"ok\",\"threshold\":5000000}},\"name\":\"test1\",\"status\":\"ok\",\"failOnError\":true}"
t3 := &Test{}
if err := json.Unmarshal([]byte(jsonStr), &t3); err != nil {
fmt.Println(err.Error())
}
fmt.Printf("%#v\n\n", t3)
fmt.Printf("%#v\n\n", t3.Thresholds)
str3, err := json.Marshal(t3)
if err != nil {
fmt.Println(err.Error())
}
fmt.Printf("%#v\n\n", string(str3))
var out2 bytes.Buffer
json.Indent(&out2, str3, "", " ")
fmt.Printf("%s\n\n", string(out2.Bytes()))
}
// You can edit this code!
// Click here and start typing.
package main
import (
"bytes"
"encoding/json"
"fmt"
"time"
)
// Custom layout for un/marshaling includes quotes
const customLayout = `"2006-01-02"`
// Standard layout for parse / format
const stdLayout = "2006-01-02"
type MyTime struct {
time.Time
}
func (t *MyTime) UnmarshalJSON(b []byte) error {
str := bytes.NewBuffer(b).String()
parsed, _ := time.Parse(customLayout, str)
*t = MyTime{parsed}
return nil
}
func (t MyTime) MarshalJSON() ([]byte, error) {
return []byte(t.Format(customLayout)), nil
}
type MyType struct {
Count int `json:"count"`
Day MyTime `json:"day"`
Name string `json:"name"`
}
func main() {
var d MyType
jsonStr := `{"count":1111,"day":"2018-02-20","name":"foo"}`
_ = json.Unmarshal([]byte(jsonStr), &d)
fmt.Printf("%+v\n", d)
d2 := MyType{Count: 1234, Day: MyTime{d.Day.AddDate(0, 0, -5)}, Name: "bar"}
fmt.Printf("%+v\n", d2)
dStr, _ := json.Marshal(d2)
fmt.Println("dStr: " + string(dStr))
fmt.Println(d2.Day.Format(stdLayout))
s, _ := time.Parse(stdLayout, "2018-01-22")
t1 := MyTime{s}
fmt.Printf("%+v\n", t1)
fmt.Println(t1.Format(stdLayout))
}
package main
import (
"encoding/json"
"errors"
"fmt"
)
type InputData struct {
// *map[string]interface{}
// or
// []map[string]interface
Data interface{} `json:"data"`
Foo string `json:"foo"`
}
func (m *InputData) UnmarshalJSON(data []byte) error {
type Alias InputData
aux := &struct {
*Alias
}{
Alias: (*Alias)(m),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
_, isObjData := aux.Data.(map[string]interface{})
if !isObjData {
_, isMapArray := aux.Data.([]map[string]interface{})
if isMapArray {
return nil
}
arrData, isArrData := aux.Data.([]interface{})
if !isArrData {
return errors.New("Unsupported type for Data")
}
if len(arrData) > 0 {
for _, elem := range arrData {
_, isObjData = elem.(map[string]interface{})
if !isObjData {
return errors.New("Unsupported type for property Data")
}
}
}
}
return nil
}
func main() {
jsonObjData := `{"foo":"bar","data":{"name":"Bob"}}`
jsonArrData := `{"foo":"bar","data":[{"name":"Bob"},{"name":"Sara"}]}`
jsonEmpty := `{"foo":"bar","data":{}}`
jsonEmptyArr := `{"foo":"bar","data":[]`
jsonArrEmpty := `{"foo":"bar","data":[{},{},{}]}`
jsonArrMix := `{"foo":"bar","data":[{},{},{"name":"Bob"}]}`
inObj, _ := createInputData(jsonObjData)
fmt.Printf("inObj:\n\t%+v\n\tdata:\t%+v\n\tT:%T\n", inObj, inObj.Data, inObj.Data)
inObjStr, _ := json.Marshal(inObj)
fmt.Println(string(inObjStr))
inArr, _ := createInputData(jsonArrData)
fmt.Printf("inArr:\n\t%+v\n\tdata:\t%+v\n\tT:%T\n", inArr, inArr.Data, inArr.Data)
sliceData := (inArr.Data).([]interface{})
fmt.Printf("%T sliceData[0]:%+v\n", sliceData[0], sliceData[0])
inArrStr, _ := json.Marshal(inArr)
fmt.Println(string(inArrStr))
inEmpty, _ := createInputData(jsonEmpty)
fmt.Printf("inEmpty:\n\t%+v\n\tdata:\t%+v\n\tT:%T\n", inEmpty, inEmpty.Data, inEmpty.Data)
inArrEmpty, _ := createInputData(jsonArrEmpty)
fmt.Printf("inArrEmpty:\n\t%+v\n\tdata:\t%+v\n\tT:%T\n", inArrEmpty, inArrEmpty.Data, inArrEmpty.Data)
sliceData = (inArrEmpty.Data).([]interface{})
fmt.Printf("%T sliceData[0]:%+v\n", sliceData[0], sliceData[0])
fmt.Printf("%T sliceData[2]:%+v\n", sliceData[0], sliceData[2])
inEmptyArr, _ := createInputData(jsonEmptyArr)
fmt.Printf("inEmptyArr:\n\t%+v\n\tdata:\t%+v\n\tT:%T\n", inEmptyArr, inEmptyArr.Data, inEmptyArr.Data)
inArrMix, _ := createInputData(jsonArrMix)
fmt.Printf("inArrMix:\n\t%+v\n\tdata:\t%+v\n\tT:%T\n", inArrMix, inArrMix.Data, inArrMix.Data)
sliceData = (inArrMix.Data).([]interface{})
fmt.Printf("%T sliceData[0]:%+v\n", sliceData[0], sliceData[0])
fmt.Printf("%T sliceData[2]:%+v\n", sliceData[0], sliceData[2])
invalidObjJson := `{"foo":"bar","data":"Bob"}`
invalidArrJson := `{"foo":"bar","data":["Bob", "Sara"]}`
invalidMixJson := `{"foo":"bar","data":[{}, {"name": "Bob"}, "Sara"]}`
inv, err := createInputData(invalidObjJson)
fmt.Printf("inv:\n\t%+v\n\terr:\t%+v\n\tT:%T\n", inv, err, inv)
inv2, err := createInputData(invalidArrJson)
fmt.Printf("inv2:\n\t%+v\n\terr:\t%+v\n\tT:%T\n", inv2, err, inv2)
inv3, err := createInputData(invalidMixJson)
fmt.Printf("inv3:\n\t%+v\n\terr:\t%+v\n\tT:%T\n", inv3, err, inv3)
}
func createInputData(input string) (*InputData, error) {
var res InputData
err := json.Unmarshal([]byte(input), &res)
return &res, err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment