Skip to content

Instantly share code, notes, and snippets.

@indraniel
Last active January 2, 2018 17:41
Show Gist options
  • Save indraniel/1d433598f6a44d67bb45 to your computer and use it in GitHub Desktop.
Save indraniel/1d433598f6a44d67bb45 to your computer and use it in GitHub Desktop.
Decoding JSON: A Refactoring from http://talks.golang.org/2015/json.slide
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)
}
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)
}
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)
}
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)
}
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)
}
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