I am writing a Minecraft library which follows the protocol specs here. I am attempting to receive the status packet. The json codec decodes and returns the unmarshalled json struct in a map. Then, I decode the map using the mapstructure package. But this requires knowing the final type of the packet so you can send it the struct for output. I don’t know how to determine the struct that matches the map returned from the decode.
Is there a better way to unmarshal the json to return the correct struct?
Here is the JSON decode method that returns the struct in a map:
func ReadJSON(reader io.Reader) (val map[string]interface{}, err error) {
var v map[string]interface{}
bytes, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
//Will unmarshal starting at the first open brace in the byte[]
err = json.Unmarshal(bytes[strings.IndexRune(string(bytes), '{'):], &v)
return v, err
}
Here is the decode method:
The original code was written by justblender I’m attempting to add struct support.
func (c *Connection) decode(p *Packet) (packets.Holder, error) {
holder, ok := packetList[p.Direction][c.State][p.ID]
if !ok {
return nil, UnknownPacketType
}
inst := reflect.New(holder).Elem()
for i := 0; i < inst.NumField(); i++ {
field := inst.Field(i)
codec, ok := field.Interface().(codecs.Codec)
if !ok {
if field.Kind() == reflect.Struct {
codec = codecs.JSON{V: field.Interface()}
} else {
return nil, codecs.UnknownCodecType
}
}
value, err := codec.Decode(&p.Data)
if err != nil {
return nil, fmt.Errorf("packet decode failed: %s", err)
}
if reflect.TypeOf(codec) == reflect.TypeOf(codecs.JSON{}) {
switch values_type {//This is the part where the struct is determined
case equals packets.StatusResponse{}:
pkt := packets.StatusResponse{}
err = mapstructure.Decode(value, &pkt.status)
if err != nil {
return nil, fmt.Errorf("mapstructure decode failed: %s", err)
}
value = pkt
}
}
field.Set(reflect.ValueOf(value))
}
return inst.Interface().(packets.Holder), nil
}
If you need more code look at justblender’s github because very little has changed.
The final goal is to take the JSON that is recieved and return the struct:
type StatusResponse struct {
Status struct {
Version struct {
Name string `json:"name"`
Protocol int `json:"protocol"`
} `json:"version"`
Players struct {
Max int `json:"max"`
Online int `json:"online"`
} `json:"players"`
Description chat.TextComponent `json:"description"`
}
}
Know that I don’t know a lot about JSON, or reflection so if I have done some stupid stuff correct politely. If there is an easier way please let me know. I am very lost and I’m just trying to learn.