29

I'm a newbie in Go and now I have a problem. I have a type called Message, it is a struct like this:

type Message struct {
    Cmd string `json:"cmd"`
    Data interface{} `json:"data"`
}

I also have a type called CreateMessage like this:

type CreateMessage struct {
    Conf map[string]int `json:"conf"`
    Info map[string]int `json:"info"`
}

And I have a JSON string like {"cmd":"create","data":{"conf":{"a":1},"info":{"b":2}}}.

When I use json.Unmarshal to decode that into a Message variable, the answer is {Cmd:create Data:map[conf:map[a:1] info:map[b:2]]}.

So could I decode the JSON into a Message struct and change its Data's interface{} to type CreateMessage according to the Cmd?

I tried to convert Data directly into type CreateMessage but the complier told me that Data is a map[sting]interface{} type.

Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
Papulatus
  • 677
  • 2
  • 8
  • 18

1 Answers1

42

Define a struct type for the fixed part of the message with a json.RawMessage field to capture the variant part of the message. Define struct types for each of the variant types and decode to them based on the the command.

type Message struct {
  Cmd string `json:"cmd"`
  Data      json.RawMessage
}  

type CreateMessage struct {
    Conf map[string]int `json:"conf"`
    Info map[string]int `json:"info"`
}  

func main() {
    var m Message
    if err := json.Unmarshal(data, &m); err != nil {
        log.Fatal(err)
    }
    switch m.Cmd {
    case "create":
        var cm CreateMessage
        if err := json.Unmarshal([]byte(m.Data), &cm); err != nil {
            log.Fatal(err)
        }
        fmt.Println(m.Cmd, cm.Conf, cm.Info)
    default:
        log.Fatal("bad command")
    }
}

playground example

Yami Odymel
  • 1,782
  • 3
  • 22
  • 48
Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242