Last active
May 16, 2018 23:53
-
-
Save bojand/5535e81bfca5d9a89f1b4078f229e1ea to your computer and use it in GitHub Desktop.
Custom JSON Marshal
This file contains hidden or 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 ( | |
"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)) | |
} |
This file contains hidden or 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" | |
"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())) | |
} |
This file contains hidden or 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
// 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)) | |
} |
This file contains hidden or 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 ( | |
"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