猿问

如何将 JSON 对象数组转换为 Go 中具有默认值的结构数组?

我正在开发一个 Go API,它可以接收由 JSON 对象数组组成的 POST。POST 的结构类似于:


[

  {

    "name":"Las Vegas",

    "size":14

  },

  {

    "valid": false,

    "name":"Buffalo",

    "size":63

  }

]  

假设我有以下结构:


type Data {

    Valid    bool

    Name     string

    Size     float64

}

我想创建一堆Datas 并Valid设置为true任何时候它实际上并未在 JSON 中指定为false. 如果我只做一个,我可以在 Go 中解析 JSON 时使用如何指定默认值,但是对于未知数量的它们,我唯一能想到的是:


var allMap []map[string]interface{}

var structs []Data

for _, item := range allMap {

  var data Data

  var v interface{}

  var ok bool

  if v, ok := item["value"]; ok {

    data.Valid = v

  } else {

    data.Valid = true

  }

  id v, ok := item["name"]; ok {

    data.Name = v

  }

  ...

  structs = append(structs, data)

}

return structs

现在我实际使用的结构有 14 个字段,其中一些具有我想分配默认值的值,其他的可以留空,但所有这些都必须使用这种方法进行迭代。


有没有更好的办法?


桃花长相依
浏览 334回答 2
2回答

九州编程

您可以使用该json.RawMessage类型来推迟解组某些 JSON 文本值。如果您使用这种类型,那么 JSON 文本将存储在其中而无需解组(因此您可以稍后根据需要解组此片段)。因此,在您的情况下,如果您尝试解组成一个这样的切片RawMessage,您可以使用您在问题中链接的技术,即您可以迭代原始值的切片(每个值的 JSON 文本Data),创建一个Datastruct 将您想要的值作为缺失值的默认值,并将切片元素解组到这个准备好的结构中。就这样。它看起来像这样:allJson := []json.RawMessage{}if err := json.Unmarshal(src, &allJson); err != nil {    panic(err)}allData := make([]Data, len(allJson))for i, v := range allJson {    // Here create your Data with default values    allData[i] = Data{Valid: true}    if err := json.Unmarshal(v, &allData[i]); err != nil {        panic(err)    }}在Go Playground上试一试。注释/变体为了提高效率(为了避免复制结构体),您还allData可以在上面的示例中使成为一个指针切片,如下所示:allData := make([]*Data, len(allJson))for i, v := range allJson {    // Here create your Data with default values    allData[i] = &Data{Valid: true}    if err := json.Unmarshal(v, allData[i]); err != nil {        panic(err)    }}如果您想继续使用非指针,为了提高效率,您可以在切片元素本身中“准备”您想要的默认值,如下所示:allData := make([]Data, len(allJson))for i, v := range allJson {    // Here set your default values in the slice elements    // Only set those which defer from the zero values:    allData[i].Valid = true    if err := json.Unmarshal(v, &allData[i]); err != nil {        panic(err)    }}

白衣染霜花

您可以通过UnmarshalJSON在您的类型上提供一种方法来使其透明并自动工作,即使您的类型在结构或切片中找到,您也可以做一个很好的技巧。func (d *Data) UnmarshalJSON(j []byte) error {    type _Data Data // Dummy type to avoid infinite recursion in UnmarshalJSON    tmp := _Data{ // Set defaults here        Valid: true,    }    err := json.Unmarshal(j, &tmp)    if err != nil {        return err    }    *d = Data(tmp)    return nil}类型的_Data存在只是为了让我们可以调用json.Unmarshal(j, &tmp)并获得原始的未覆盖行为,而不是调用UnmarshalJSON我们已经在中间的方法。我们可以tmp使用您已经链接到的技巧设置默认值。然后在解组完成后,我们可以转换tmp为,Data因为毕竟Data和_Data确实是相同的类型。鉴于这种方法,您可以简单地var structs []Dataerr := json.Unmarshal(input, &structs)(或同样使用 a json.Decoder)并让它按照您想要的方式工作。
随时随地看视频慕课网APP

相关分类

Go
我要回答