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

时间:2016-08-03 17:21:17

标签: json go amazon-s3

我正在尝试更改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"`
}

适用于放置存储桶策略的方法。当我尝试获取当前策略并对其进行修改时,问题就出现了。

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

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

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

2 个答案:

答案 0 :(得分:2)

作为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
}

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

MaybeSlice是一个可怕的名字,但希望你能得到这个想法。

使用struct类型可以更轻松地使用这些自定义方法,但我认为上述方法是正确的。如果我没记错,您需要Action *MaybeSlice使用上述内容。

答案 1 :(得分:1)

如果您要解析的字段类型无法保证,请使用interface{}

type Statement struct {
    Sid       string    `json:"Sid"`
    Effect    string    `json:"Effect"`
    Principal Principal `json:"Principal"`

    Action   interface{} `json:"Action"`
    Resource interface{} `json:"Resource"`
}

使用类型开关访问基础原始数据类型:

//Example: Trying to access Action member of a statement myStatement.
switch a := myStatement.Action.(type) {
    case []string:
        //Action is a slice. Handle it accordingly.
    case string:
        //Action is a string. Handle it accordingly.
    default:
        //Some other datatype that can be returned by aws?
}

或者你可以为两种情况都有单独的结构,如果Unmarshaling一个失败,将它解组到另一个结构中,就像这样:

err := json.Unmarshal(jsonStr, &struct1)
if err != nil {
    fmt.Println(err)
    err = json.Unmarshal(jsonStr, &struct2)
}