如何解码为嵌入式结构?

How can I decode to an embedded struct?

我希望能够在响应主体上使用 .Decode() 来填充结构,而不必首先尝试找出我应该解码为哪种类型的结构。

我有一个通用结构 Match 来保存有关已玩游戏的信息,例如Fortnite 中的一场比赛。在这个结构中,我使用 MatchData 来保存整个游戏的比赛数据。

当解码到 MatchData 结构时,我发现底层嵌入类型已初始化,但所有默认值均已初始化,而不是响应中的值。

type Match struct {
    MatchID       int        `json:"match_id"`
    GameType      int        `json:"game_type"`
    MatchData     *MatchData `json:"match_data"`
}

type MatchData struct {
    MatchGame1
    MatchGame2
}

type MatchGame1 struct {
    X int `json:"x"`
    Y int `json:"y"`
}

type MatchGame2 struct {
    X int `json:"x"`
    Y int `json:"y"`
}

func populateData(m *Match) (Match, error) {
    response, err := http.Get("game1.com/path")
    if err != nil {
        return nil, err
    }
    
    // Here, m.MatchData is set with X and Y equal to 0
    // when response contains values > 0
    err = json.NewDecoder(response.Body).Decode(&m.MatchData)
    if err != nil {
        return nil, err
    }

    return m, nil
}

编辑 示例预期 JSON 有效负载。

{
    "x": 10,
    "y": 20
}

我可以通过检查 m.GameType,创建一个对应的结构然后将其分配给 m.MatchData 来解决问题,但是如果我想添加另外 100 个游戏 API,我更愿意该功能可能与它无关。

我不确定这是否可行,但在此先感谢。

问题中的方法将不起作用,因为嵌入式结构共享字段名称。试试这个方法。

声明一个映射,将游戏类型标识符与关联的围棋类型相关联。这只是与解码有关的代码,了解数百种游戏类型。

var gameTypes = map[int]reflect.Type{
    1: reflect.TypeOf(&MatchGame1{}),
    2: reflect.TypeOf(&MatchGame2{}),
}

将匹配数据解码为raw message。使用游戏类型创建匹配数据值并解码为该值。

func decodeMatch(r io.Reader) (*Match, error) {

    // Start with match data set to a raw messae.
    var raw json.RawMessage
    m := &Match{MatchData: &raw}

    err := json.NewDecoder(r).Decode(m)
    if err != nil {
        return nil, err
    }

    m.MatchData = nil

    // We are done if there's no raw message.
    if len(raw) == 0 {
        return m, nil
    }

    // Create the required match data value.
    t := gameTypes[m.GameType]
    if t == nil {
        return nil, errors.New("unknown game type")
    }
    m.MatchData = reflect.New(t.Elem()).Interface()

    // Decode the raw message to the match data.
    return m, json.Unmarshal(raw, m.MatchData)

}

Run it on the playground.