Last active
January 2, 2018 17:41
-
-
Save indraniel/1d433598f6a44d67bb45 to your computer and use it in GitHub Desktop.
Decoding JSON: A Refactoring from http://talks.golang.org/2015/json.slide
This file contains 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 | |
// v1 -- decoding JSON via a simple map | |
import ( | |
"encoding/json" | |
"fmt" | |
"log" | |
"strings" | |
"time" | |
) | |
type Person struct { | |
Name string | |
Born time.Time | |
Size ShirtSize | |
} | |
type ShirtSize byte | |
const ( | |
NA ShirtSize = iota | |
XS | |
S | |
M | |
L | |
XL | |
) | |
func (p *Person) Parse(s string) error { | |
fields := map[string]string{} | |
dec := json.NewDecoder(strings.NewReader(s)) | |
if err := dec.Decode(&fields); err != nil { | |
return fmt.Errorf("decode person: %v", err) | |
} | |
p.Name = fields["name"] | |
born, err := time.Parse("2006/01/02", fields["birthdate"]) | |
if err != nil { | |
return fmt.Errorf("invalid date: %v", err) | |
} | |
p.Born = born | |
p.Size, err = ParseShirtSize(fields["shirt-size"]) | |
return nil | |
} | |
func ParseShirtSize(s string) (ShirtSize, error) { | |
sizes := map[string]ShirtSize{ | |
"XS": XS, "S": S, "M": M, "L": L, "XL": XL, | |
} | |
ss, ok := sizes[s] | |
if !ok { | |
return NA, fmt.Errorf("invalid ShirtSize %q", s) | |
} | |
return ss, nil | |
} | |
func main() { | |
now := time.Now() | |
fmt.Printf("Standard format: %v\n", now) | |
fmt.Printf("American format: %v\n", now.Format("Jan 2 2006")) | |
fmt.Printf("European format: %v\n", now.Format("02/01/2006")) | |
input := ` | |
{ | |
"name" : "Gopher", | |
"birthdate": "2009/11/10", | |
"shirt-size": "XS" | |
} | |
` | |
var p Person | |
if err := p.Parse(input); err != nil { | |
log.Fatalf("parse person: %v", err) | |
} | |
fmt.Println(p) | |
} |
This file contains 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 | |
// v2 -- decoding JSON via an auxiliary struct | |
import ( | |
"encoding/json" | |
"fmt" | |
"log" | |
"strings" | |
"time" | |
) | |
type Person struct { | |
Name string `json:"name"` | |
Born time.Time `json:"birthdate"` | |
Size ShirtSize `json:"shirt-size"` | |
} | |
type ShirtSize byte | |
const ( | |
NA ShirtSize = iota | |
XS | |
S | |
M | |
L | |
XL | |
) | |
func (p *Person) Parse(s string) error { | |
var aux struct { | |
Name string | |
Born string `json:"birthdate"` | |
Size string `json:"shirt-size"` | |
} | |
dec := json.NewDecoder(strings.NewReader(s)) | |
if err := dec.Decode(&aux); err != nil { | |
return fmt.Errorf("decode person: %v", err) | |
} | |
p.Name = aux.Name | |
born, err := time.Parse("2006/01/02", aux.Born) | |
if err != nil { | |
return fmt.Errorf("invalid date: %v", err) | |
} | |
p.Born = born | |
p.Size, err = ParseShirtSize(aux.Size) | |
return err | |
} | |
func ParseShirtSize(s string) (ShirtSize, error) { | |
sizes := map[string]ShirtSize{ | |
"XS": XS, "S": S, "M": M, "L": L, "XL": XL, | |
} | |
ss, ok := sizes[s] | |
if !ok { | |
return NA, fmt.Errorf("invalid ShirtSize %q", s) | |
} | |
return ss, nil | |
} | |
func main() { | |
now := time.Now() | |
fmt.Printf("Standard format: %v\n", now) | |
fmt.Printf("American format: %v\n", now.Format("Jan 2 2006")) | |
fmt.Printf("European format: %v\n", now.Format("02/01/2006")) | |
input := ` | |
{ | |
"name" : "Gopher", | |
"birthdate": "2009/11/10", | |
"shirt-size": "XS" | |
} | |
` | |
var p Person | |
if err := p.Parse(input); err != nil { | |
log.Fatalf("parse person: %v", err) | |
} | |
fmt.Println(p) | |
} |
This file contains 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 | |
// v3 -- UnmarshalJSON on the Person struct | |
import ( | |
"bytes" | |
"encoding/json" | |
"fmt" | |
"log" | |
"strings" | |
"time" | |
) | |
type Person struct { | |
Name string `json:"name"` | |
Born time.Time `json:"birthdate"` | |
Size ShirtSize `json:"shirt-size"` | |
} | |
type ShirtSize byte | |
const ( | |
NA ShirtSize = iota | |
XS | |
S | |
M | |
L | |
XL | |
) | |
func (p *Person) UnmarshalJSON(data []byte) error { | |
var aux struct { | |
Name string | |
Born string `json:"birthdate"` | |
Size string `json:"shirt-size"` | |
} | |
dec := json.NewDecoder(bytes.NewReader(data)) | |
if err := dec.Decode(&aux); err != nil { | |
return fmt.Errorf("decode person: %v", err) | |
} | |
p.Name = aux.Name | |
born, err := time.Parse("2006/01/02", aux.Born) | |
if err != nil { | |
return fmt.Errorf("invalid date: %v", err) | |
} | |
p.Born = born | |
p.Size, err = ParseShirtSize(aux.Size) | |
return err | |
} | |
func ParseShirtSize(s string) (ShirtSize, error) { | |
sizes := map[string]ShirtSize{ | |
"XS": XS, "S": S, "M": M, "L": L, "XL": XL, | |
} | |
ss, ok := sizes[s] | |
if !ok { | |
return NA, fmt.Errorf("invalid ShirtSize %q", s) | |
} | |
return ss, nil | |
} | |
func main() { | |
now := time.Now() | |
fmt.Printf("Standard format: %v\n", now) | |
fmt.Printf("American format: %v\n", now.Format("Jan 2 2006")) | |
fmt.Printf("European format: %v\n", now.Format("02/01/2006")) | |
input := ` | |
{ | |
"name" : "Gopher", | |
"birthdate": "2009/11/10", | |
"shirt-size": "XS" | |
} | |
` | |
var p Person | |
dec := json.NewDecoder(strings.NewReader(input)) | |
if err := dec.Decode(&p); err != nil { | |
log.Fatalf("parse person: %v", err) | |
} | |
fmt.Println(p) | |
} |
This file contains 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 | |
// v4 -- UnmarshalJSON for enums (UnmarshalJSON on the ShirtSize struct) | |
import ( | |
"bytes" | |
"encoding/json" | |
"fmt" | |
"log" | |
"strings" | |
"time" | |
) | |
type Person struct { | |
Name string `json:"name"` | |
Born time.Time `json:"birthdate"` | |
Size ShirtSize `json:"shirt-size"` | |
} | |
type ShirtSize byte | |
const ( | |
NA ShirtSize = iota | |
XS | |
S | |
M | |
L | |
XL | |
) | |
func (p *Person) UnmarshalJSON(data []byte) error { | |
var aux struct { | |
Name string | |
Born string `json:"birthdate"` | |
Size ShirtSize `json:"shirt-size"` | |
} | |
dec := json.NewDecoder(bytes.NewReader(data)) | |
if err := dec.Decode(&aux); err != nil { | |
return fmt.Errorf("decode person: %v", err) | |
} | |
p.Name = aux.Name | |
born, err := time.Parse("2006/01/02", aux.Born) | |
if err != nil { | |
return fmt.Errorf("invalid date: %v", err) | |
} | |
p.Born = born | |
return err | |
} | |
func (ss *ShirtSize) UnmarshalJSON(data []byte) error { | |
var s string | |
if err := json.Unmarshal(data, &s); err != nil { | |
return fmt.Errorf("shirt-size should be a string, got %s", data) | |
} | |
sizes := map[string]ShirtSize{ | |
"XS": XS, "S": S, "M": M, "L": L, "XL": XL, | |
} | |
got, ok := sizes[s] | |
if !ok { | |
return fmt.Errorf("invalid ShirtSize %q", s) | |
} | |
*ss = got | |
return nil | |
} | |
func main() { | |
now := time.Now() | |
fmt.Printf("Standard format: %v\n", now) | |
fmt.Printf("American format: %v\n", now.Format("Jan 2 2006")) | |
fmt.Printf("European format: %v\n", now.Format("02/01/2006")) | |
input := ` | |
{ | |
"name" : "Gopher", | |
"birthdate": "2009/11/10", | |
"shirt-size": "XS" | |
} | |
` | |
var p Person | |
dec := json.NewDecoder(strings.NewReader(input)) | |
if err := dec.Decode(&p); err != nil { | |
log.Fatalf("parse person: %v", err) | |
} | |
fmt.Println(p) | |
} |
This file contains 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 | |
// v5 -- UnmarshalJSON on the Date (UnmarshalJSON on the newly created Date struct) | |
import ( | |
"bytes" | |
"encoding/json" | |
"fmt" | |
"log" | |
"strings" | |
"time" | |
) | |
type Person struct { | |
Name string `json:"name"` | |
Born time.Time `json:"birthdate"` | |
Size ShirtSize `json:"shirt-size"` | |
} | |
type ShirtSize byte | |
type Date struct { | |
time.Time | |
} | |
const ( | |
NA ShirtSize = iota | |
XS | |
S | |
M | |
L | |
XL | |
) | |
func (p *Person) UnmarshalJSON(data []byte) error { | |
var aux struct { | |
Name string | |
Born Date `json:"birthdate"` | |
Size ShirtSize `json:"shirt-size"` | |
} | |
dec := json.NewDecoder(bytes.NewReader(data)) | |
if err := dec.Decode(&aux); err != nil { | |
return fmt.Errorf("decode person: %v", err) | |
} | |
p.Name = aux.Name | |
p.Size = aux.Size | |
p.Born = aux.Born.Time | |
return nil | |
} | |
func (d *Date) UnmarshalJSON(data []byte) error { | |
var s string | |
if err := json.Unmarshal(data, &s); err != nil { | |
return fmt.Errorf("birthdate should be a string, got %s", data) | |
} | |
born, err := time.Parse("2006/01/02", s) | |
if err != nil { | |
return fmt.Errorf("invalid date: %v", err) | |
} | |
d.Time = born | |
return nil | |
} | |
func (ss *ShirtSize) UnmarshalJSON(data []byte) error { | |
var s string | |
if err := json.Unmarshal(data, &s); err != nil { | |
return fmt.Errorf("shirt-size should be a string, got %s", data) | |
} | |
sizes := map[string]ShirtSize{ | |
"XS": XS, "S": S, "M": M, "L": L, "XL": XL, | |
} | |
got, ok := sizes[s] | |
if !ok { | |
return fmt.Errorf("invalid ShirtSize %q", s) | |
} | |
*ss = got | |
return nil | |
} | |
func main() { | |
now := time.Now() | |
fmt.Printf("Standard format: %v\n", now) | |
fmt.Printf("American format: %v\n", now.Format("Jan 2 2006")) | |
fmt.Printf("European format: %v\n", now.Format("02/01/2006")) | |
input := ` | |
{ | |
"name" : "Gopher", | |
"birthdate": "2009/11/10", | |
"shirt-size": "XS" | |
} | |
` | |
var p Person | |
dec := json.NewDecoder(strings.NewReader(input)) | |
if err := dec.Decode(&p); err != nil { | |
log.Fatalf("parse person: %v", err) | |
} | |
fmt.Println(p) | |
} |
This file contains 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 | |
// v6 -- Removing the UnmarshalJSON on the Person struct -- its struct attributes are now Unmarshal-able | |
import ( | |
"encoding/json" | |
"fmt" | |
"log" | |
"strings" | |
"time" | |
) | |
type Person struct { | |
Name string `json:"name"` | |
Born Date `json:"birthdate"` | |
Size ShirtSize `json:"shirt-size"` | |
} | |
type ShirtSize byte | |
type Date struct { | |
time.Time | |
} | |
const ( | |
NA ShirtSize = iota | |
XS | |
S | |
M | |
L | |
XL | |
) | |
func (d *Date) UnmarshalJSON(data []byte) error { | |
var s string | |
if err := json.Unmarshal(data, &s); err != nil { | |
return fmt.Errorf("birthdate should be a string, got %s", data) | |
} | |
born, err := time.Parse("2006/01/02", s) | |
if err != nil { | |
return fmt.Errorf("invalid date: %v", err) | |
} | |
d.Time = born | |
return nil | |
} | |
func (ss *ShirtSize) UnmarshalJSON(data []byte) error { | |
var s string | |
if err := json.Unmarshal(data, &s); err != nil { | |
return fmt.Errorf("shirt-size should be a string, got %s", data) | |
} | |
sizes := map[string]ShirtSize{ | |
"XS": XS, "S": S, "M": M, "L": L, "XL": XL, | |
} | |
got, ok := sizes[s] | |
if !ok { | |
return fmt.Errorf("invalid ShirtSize %q", s) | |
} | |
*ss = got | |
return nil | |
} | |
func main() { | |
now := time.Now() | |
fmt.Printf("Standard format: %v\n", now) | |
fmt.Printf("American format: %v\n", now.Format("Jan 2 2006")) | |
fmt.Printf("European format: %v\n", now.Format("02/01/2006")) | |
input := ` | |
{ | |
"name" : "Gopher", | |
"birthdate": "2009/11/10", | |
"shirt-size": "XS" | |
} | |
` | |
var p Person | |
dec := json.NewDecoder(strings.NewReader(input)) | |
if err := dec.Decode(&p); err != nil { | |
log.Fatalf("parse person: %v", err) | |
} | |
fmt.Println(p) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment