refactor: marshaler unmarshaler iface

This commit is contained in:
Inanc Gumus
2019-10-21 15:21:34 +03:00
parent 59865eac28
commit 3ac59459fd
13 changed files with 83 additions and 38 deletions

View File

@ -7,9 +7,7 @@
package main package main
import ( import "encoding/json"
"encoding/json"
)
func encode() ([]byte, error) { func encode() ([]byte, error) {
l := list{ l := list{
@ -17,13 +15,12 @@ func encode() ([]byte, error) {
{Title: "odyssey", Price: 15, Released: toTimestamp("733622400")}, {Title: "odyssey", Price: 15, Released: toTimestamp("733622400")},
{Title: "hobbit", Price: 25}, {Title: "hobbit", Price: 25},
} }
return json.MarshalIndent(l, "", "\t") return json.MarshalIndent(l, "", "\t")
} }
func decode(data []byte) (l list, err error) { func decode(data []byte) (l list, _ error) {
if err := json.Unmarshal(data, &l); err != nil { if err := json.Unmarshal(data, &l); err != nil {
return nil, err return nil, err
} }
return l, nil return
} }

View File

@ -8,6 +8,7 @@
package main package main
import ( import (
"sort"
"strings" "strings"
) )
@ -34,16 +35,20 @@ func (l list) discount(ratio float64) {
} }
} }
// implementation of the sort.Interface:
// by default `list` sorts by `Title`. // by default `list` sorts by `Title`.
func (l list) Len() int { return len(l) } func (l list) Len() int { return len(l) }
func (l list) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l list) Less(i, j int) bool { return l[i].Title < l[j].Title } func (l list) Less(i, j int) bool { return l[i].Title < l[j].Title }
func (l list) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
// byRelease sorts by product release dates. // byRelease sorts by product release dates.
type byRelease struct { type byRelease struct{ list }
list
}
func (bp byRelease) Less(i, j int) bool { func (bp byRelease) Less(i, j int) bool {
return bp.list[i].Released.Before(bp.list[j].Released.Time) return bp.list[i].Released.Before(bp.list[j].Released.Time)
} }
func byReleaseDate(l list) sort.Interface {
return &byRelease{l}
}

View File

@ -9,13 +9,45 @@ package main
import ( import (
"fmt" "fmt"
"log"
) )
func main() { func main() {
// removed error handling for brevity (normally you shouldn't do this). // First encode products as JSON:
out, _ := encode() data, err := encode()
fmt.Println(string(out)) if err != nil {
log.Fatalln(err)
}
fmt.Println(string(data))
l, _ := decode(out) // Then decode them back from JSON:
l, err := decode(data)
if err != nil {
log.Fatalln(err)
}
// Let the list value print itself:
fmt.Print(l) fmt.Print(l)
} }
/*
Summary:
- json.Marshal() and json.MarshalIndent() can only encode primitive types.
- It cannot encode custom types.
- Implement the json.Marshaler interface and teach it how to encode your custom types.
- See time.Time code: It satisfies the json.Marshaler interface.
- json.Unmarshal() can only decode primitive types.
- It cannot decode custom types.
- Implement the json.Unmarshaler interface and teach it how to decode your custom types.
- See time.Time code: It satisfies the json.Unmarshaler interface as well.
- strconv.AppendInt() can append an int value to a []byte.
- There are several other functions in the strconv package for other primitive types as well.
- Do not make unnecessary string <-> []byte conversions.
- log.Fatalln() can print the given error message and terminate the program.
- Use it only from the main(). Do not use it in other functions.
- main() is should be the main driver of a program.
*/

View File

@ -14,7 +14,7 @@ import (
type product struct { type product struct {
Title string `json:"title"` Title string `json:"title"`
Price money `json:"price"` Price money `json:"price"`
Released timestamp `json:"released,omitempty"` Released timestamp `json:"released"`
} }
func (p *product) String() string { func (p *product) String() string {

View File

@ -16,6 +16,8 @@ type timestamp struct {
time.Time time.Time
} }
// implementation of the fmt.Stringer interface:
func (ts timestamp) String() string { func (ts timestamp) String() string {
if ts.IsZero() { if ts.IsZero() {
return "unknown" return "unknown"
@ -26,8 +28,9 @@ func (ts timestamp) String() string {
return ts.Format(layout) return ts.Format(layout)
} }
// implementation of the json.Marshaler and json.Unmarshaler interfaces:
// timestamp knows how to decode itself from json. // timestamp knows how to decode itself from json.
// UnmarshalJSON is an implementation of the json.Unmarshaler interface.
// json.Unmarshal and json.Decode call this method. // json.Unmarshal and json.Decode call this method.
func (ts *timestamp) UnmarshalJSON(data []byte) error { func (ts *timestamp) UnmarshalJSON(data []byte) error {
*ts = toTimestamp(string(data)) *ts = toTimestamp(string(data))
@ -35,11 +38,9 @@ func (ts *timestamp) UnmarshalJSON(data []byte) error {
} }
// timestamp knows how to encode itself to json. // timestamp knows how to encode itself to json.
// MarshalJSON is an implementation of the json.Marshaler interface.
// json.Marshal and json.Encode call this method. // json.Marshal and json.Encode call this method.
func (ts timestamp) MarshalJSON() (out []byte, err error) { func (ts timestamp) MarshalJSON() (data []byte, _ error) {
// return ts.Time.MarshalJSON() return strconv.AppendInt(data, ts.Unix(), 10), nil
return strconv.AppendInt(out, ts.Unix(), 10), nil
} }
func toTimestamp(v interface{}) (ts timestamp) { func toTimestamp(v interface{}) (ts timestamp) {

View File

@ -24,11 +24,11 @@ func encode() ([]byte, error) {
return json.MarshalIndent(l, "", "\t") return json.MarshalIndent(l, "", "\t")
} }
func decode(data []byte) (l list, err error) { func decode(data []byte) (l list, _ error) {
if err := json.Unmarshal(data, &l); err != nil { if err := json.Unmarshal(data, &l); err != nil {
return nil, err return nil, err
} }
return l, nil return
} }
func decodeFile(path string) (list, error) { func decodeFile(path string) (list, error) {

View File

@ -8,6 +8,7 @@
package main package main
import ( import (
"sort"
"strings" "strings"
) )
@ -34,16 +35,20 @@ func (l list) discount(ratio float64) {
} }
} }
// by default `list` sorts by `Title`. // implementation of the sort.Interface:
// by default `list` sorts by `title`.
func (l list) Len() int { return len(l) } func (l list) Len() int { return len(l) }
func (l list) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l list) Less(i, j int) bool { return l[i].Title < l[j].Title } func (l list) Less(i, j int) bool { return l[i].Title < l[j].Title }
func (l list) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
// byRelease sorts by product release dates. // byRelease sorts by product release dates.
type byRelease struct { type byRelease struct{ list }
list
}
func (bp byRelease) Less(i, j int) bool { func (bp byRelease) Less(i, j int) bool {
return bp.list[i].Released.Before(bp.list[j].Released.Time) return bp.list[i].Released.Before(bp.list[j].Released.Time)
} }
func byReleaseDate(l list) sort.Interface {
return &byRelease{l}
}

View File

@ -14,7 +14,7 @@ import (
type product struct { type product struct {
Title string `json:"title"` Title string `json:"title"`
Price money `json:"price"` Price money `json:"price"`
Released timestamp `json:"released,omitempty"` Released timestamp `json:"released"`
} }
func (p *product) String() string { func (p *product) String() string {

View File

@ -31,7 +31,7 @@ func (ts *timestamp) UnmarshalJSON(data []byte) error {
return nil return nil
} }
func (ts timestamp) MarshalJSON() (out []byte, err error) { func (ts timestamp) MarshalJSON() (out []byte, _ error) {
return strconv.AppendInt(out, ts.Unix(), 10), nil return strconv.AppendInt(out, ts.Unix(), 10), nil
} }

View File

@ -24,11 +24,11 @@ func encode() ([]byte, error) {
return json.MarshalIndent(l, "", "\t") return json.MarshalIndent(l, "", "\t")
} }
func decode(data []byte) (l list, err error) { func decode(data []byte) (l list, _ error) {
if err := json.Unmarshal(data, &l); err != nil { if err := json.Unmarshal(data, &l); err != nil {
return nil, err return nil, err
} }
return l, nil return
} }
func decodeFile(path string) (list, error) { func decodeFile(path string) (list, error) {

View File

@ -8,6 +8,7 @@
package main package main
import ( import (
"sort"
"strings" "strings"
) )
@ -34,16 +35,20 @@ func (l list) discount(ratio float64) {
} }
} }
// by default `list` sorts by `Title`. // implementation of the sort.Interface:
// by default `list` sorts by `title`.
func (l list) Len() int { return len(l) } func (l list) Len() int { return len(l) }
func (l list) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l list) Less(i, j int) bool { return l[i].Title < l[j].Title } func (l list) Less(i, j int) bool { return l[i].Title < l[j].Title }
func (l list) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
// byRelease sorts by product release dates. // byRelease sorts by product release dates.
type byRelease struct { type byRelease struct{ list }
list
}
func (bp byRelease) Less(i, j int) bool { func (bp byRelease) Less(i, j int) bool {
return bp.list[i].Released.Before(bp.list[j].Released.Time) return bp.list[i].Released.Before(bp.list[j].Released.Time)
} }
func byReleaseDate(l list) sort.Interface {
return &byRelease{l}
}

View File

@ -14,7 +14,7 @@ import (
type product struct { type product struct {
Title string `json:"title"` Title string `json:"title"`
Price money `json:"price"` Price money `json:"price"`
Released timestamp `json:"released,omitempty"` Released timestamp `json:"released"`
} }
func (p *product) String() string { func (p *product) String() string {

View File

@ -31,7 +31,7 @@ func (ts *timestamp) UnmarshalJSON(data []byte) error {
return nil return nil
} }
func (ts timestamp) MarshalJSON() (out []byte, err error) { func (ts timestamp) MarshalJSON() (out []byte, _ error) {
return strconv.AppendInt(out, ts.Unix(), 10), nil return strconv.AppendInt(out, ts.Unix(), 10), nil
} }