用于 json 的 Golang 结构,允许值可选地为数组

are*_*ewm 5 json amazon-s3 go

我正在尝试更改 aws 上 s3 存储桶的策略。我为策略创建了以下 json 结构:

type Policy struct {
    Version     string  `json:"Version"`
    Id          string  `json:"Id"`
    Statement   []Statement `json:"Statement"`
}

type Statement struct {
    Sid         string      `json:"Sid"`
    Effect      string      `json:"Effect"`
    Principal   Principal   `json:"Principal"`
    Action      []string    `json:"Action"`
    Resource    []string    `json:"Resource"`
}

type Principal struct {
    AWS[]string `json:"AWS"`
}
Run Code Online (Sandbox Code Playgroud)

这适用于将存储桶策略落实到位。当我尝试获取当前策略并对其进行修改时,问题就出现了。

如果有一条语句只有一个 AWS、Action 或 Resource 值,Amazon 会将其从数组转换为简单值,从而导致我的解组失败。

有什么方法可以将 AWS/Action/Resource 值指定为字符串切片或只是字符串?


我知道有一些可用的包可以在某种程度上解决这个问题(github.com/Jeffail/gabs例如),但是创建 JSON 结构会更清晰,因为它相当简单。

vou*_*rus 3

作为 的替代方案interface{},您可以创建一个名为 MaybeSlice 的类型并在其上实现自定义 MarshalJSON 和 UnmarshalJSON 方法。

type MaybeSlice []string

func (ms *MaybeSlice) MarshalJSON() ([]byte, error) {
    // Use normal json.Marshal for subtypes
    if len(*ms) == 1 {
        return json.Marshal(([]string)(*ms)[0])
    }
    return json.Marshal(*ms)
}

func (ms *MaybeSlice) UnmarshalJSON(data []byte) error {
    // Use normal json.Unmarshal for subtypes
    var s string
    if err := json.Unmarshal(data, &s); err != nil {
        var v []string
        if err := json.Unmarshal(data, &v); err != nil {
             return err
        }
        *ms = v
        return nil
    }
    *ms = []string{s}
    return nil
}
Run Code Online (Sandbox Code Playgroud)

通过实现这些方法,MaybeSlice 类型将满足 json.Marshal 和 json.Unmarshal 所期望的接口,因此您不需要为所有类型实现自定义编组器,只需为这一类型实现。

MaybeSlice 是一个糟糕的名字,但希望你能明白。

这些自定义方法对于结构类型来说更容易,但我认为上面是正确的。如果我没记错的话,你需要制作Action一个*MaybeSlice才能使用上面的内容。