lundi 3 février 2020

How can I reduce repetitive code associated with unmarshalling dynamic types from JSON and sending to a related channel?

Consider the example below, it accepts a JSON message which is eventually unmarshalled into several possible types. How can I reduce or remove the boilerplate code associated with adding another event type.


package main

import (
    "encoding/json"
    "fmt"
)

const input = `
{
    "type": "hello",
    "event": {
        "name": "Picard"
    }
}
`

type EventEnvelope struct {
    Type  string
    Event interface{}
}

type EventHello struct {
    Name string
}

type EventHowdy struct {
    Name string
}

type Display struct {
    formal  chan EventHello
    western chan EventHowdy
}

func newDisplay() *Display {
    return &Display{
        formal:  make(chan EventHello),
        western: make(chan EventHowdy),
    }
}

func (display *Display) run() {
    for {
        select {
        case formal := <-display.formal:
            fmt.Println("Hello", formal.Name)
        case western := <-display.western:
            fmt.Println("Howdy", western.Name)
        }
    }
}

func main() {
    var event json.RawMessage
    env := EventEnvelope{
        Event: &event,
    }
    if err := json.Unmarshal([]byte(input), &env); err != nil {
        fmt.Print(err)
    }

    display := newDisplay()
    go display.run()
    events(display, event, env.Type)
}

func events(display *Display, raw json.RawMessage, event string) {
    switch event {
    case "hello":
        hello := EventHello{}
        if err := json.Unmarshal(raw, &hello); err != nil {
            fmt.Println(err)
        } else {
            display.formal <- hello
        }
    case "howdy":
        howdy := EventHowdy{}
        if err := json.Unmarshal(raw, &howdy); err != nil {
            fmt.Println(err)
        } else {
            display.western <- howdy
        }
    default:
        fmt.Println("No event handler")
    }

}

After unmarshalling the EventEnvelope, the actual event is left as a RawMessage. The event RawMessage is then unmarshalled into a specific type. Can this be dynamic? Only if there were no errors should we send it to the channel.

        hello := EventHello{}
        if err := json.Unmarshal(raw, &hello); err != nil {
            fmt.Println(err)
        } else {
            display.formal <- hello
        }

Same code in the playground: https://play.golang.org/p/WPC8JAFyxgq

Aucun commentaire:

Enregistrer un commentaire