Tags: #go #json #serialization
// https://play.golang.org/p/_lYDvs0Ukux
//
// Demonstrates how to handle a complex data structure that doesn't work well with golang.
// We write a custom unmarshal that puts the data into a more appropriate data structure.
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
var d DomainValidationResult
err := json.Unmarshal([]byte(data), &d)
log.Printf("%+v (err %v)", h, err)
}
const data = `
[
{
"comment": "",
"name": "www.example.com",
"service_id": "SU1Z0isxPaozGVKXdv0eY",
"version": 1,
"created_at": "2020-03-15T20:10:09.000Z",
"updated_at": "2020-03-15T20:10:09.000Z",
"deleted_at": null
},
"global.prod.fastly.net.",
true
]
`
// DomainValidationResult defines an idiomatic representation of the API
// response.
type DomainValidationResult struct {
Metadata DomainMetadata
Name string
Valid bool
}
// UnmarshalJSON works around the badly designed API response by coercing the
// raw data into a more appropriate data structure.
func (d *DomainValidationResult) UnmarshalJSON(data []byte) error {
var tuple []json.RawMessage
if err := json.Unmarshal(data, &tuple); err != nil {
return fmt.Errorf("initial: %w", err)
}
if want, have := 3, len(tuple); want != have {
return fmt.Errorf("unexpected array length: want %d, have %d", want, have)
}
if err := json.Unmarshal(tuple[0], &d.Metadata); err != nil {
return fmt.Errorf("metadata: %w", err)
}
if err := json.Unmarshal(tuple[1], &d.Name); err != nil {
return fmt.Errorf("name: %w", err)
}
if err := json.Unmarshal(tuple[2], &d.Valid); err != nil {
return fmt.Errorf("valid: %w", err)
}
return nil
}
// DomainMetadata represents a domain name configured for a Fastly service.
type DomainMetadata struct {
Comment string `json:"comment"`
Name string `json:"name"`
ServiceID string `json:"service_id"`
Version int `json:"version"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at"`
}
package main
import (
"encoding/json"
"fmt"
)
func main() {
var v Component
if err := json.Unmarshal(data, &v); err != nil {
panic(err)
}
fmt.Printf("%#v\n", v)
}
var data = []byte(`{"type": "foo", "config": {"name": "test"}}`)
// THE OTHER TYPE FOR COMPARISON >> var data = []byte(`{"type": "bar", "config": {"value": 123}}`)
type Component struct {
Type string `json:"type"`
Config interface{} `json:"config"`
}
func (c *Component) UnmarshalJSON(data []byte) error {
var v struct {
Type string `json:"type"`
Data json.RawMessage `json:"config"`
}
if err := json.Unmarshal(data, &v); err != nil {
return err
}
c.Type = v.Type
switch v.Type {
case "foo":
var f FooConfig
if err := json.Unmarshal(v.Data, &f); err != nil {
return err
}
c.Config = f
case "bar":
var b BarConfig
if err := json.Unmarshal(v.Data, &b); err != nil {
return err
}
c.Config = b
}
return nil
}
type FooConfig struct {
Name string `json:"name"`
}
type BarConfig struct {
Value int `json:"value"`
}