为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
桃花长相依 2021-11-15 15:49:26
我正在开发一个 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 []Datafor _, 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 个字段,其中一些具有我想分配默认值的值,其他的可以留空,但所有这些都必须使用这种方法进行迭代。有没有更好的办法?
查看完整描述

2 回答

?
九州编程

TA贡献1785条经验 获得超4个赞

您可以使用该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)

    }

}


查看完整回答
反对 回复 2021-11-15
?
白衣染霜花

TA贡献1796条经验 获得超10个赞

您可以通过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 []Data

err := json.Unmarshal(input, &structs)

(或同样使用 a json.Decoder)并让它按照您想要的方式工作。


查看完整回答
反对 回复 2021-11-15
  • 2 回答
  • 0 关注
  • 333 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信