如何在Go中使用默认值将JSON对象数组转换为结构数组?

问题描述 投票:2回答:2

我正在开发一个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。如果我做一个单一的我可以使用How to specify default values when parsing JSON in Go,但是为了做一个未知数量的他们我唯一能够想到的是:

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个字段,其中一些有我想要分配默认值的值,有些可以留空,但所有这些都必须通过使用这种方法进行迭代。

有没有更好的办法?

json go struct slice
2个回答
3
投票

您可以使用json.RawMessage类型来推迟解组某些JSON文本值。如果您使用此类型,则JSON文本将存储在此类中而不进行解组(因此您可以根据需要随后解组此片段)。

因此,在您的情况下,如果您尝试解组为这样的RawMessage片段,您可以使用您在问题中链接的技术,即您可以迭代原始值的片段(这是每个Data的JSON文本),创建一个Data结构,其中包含您想要的值作为缺失值的默认值,并将切片元素解组到此准备好的结构中。就这样。

它看起来像这样:

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)
    }
}

0
投票

您可以通过在类型上提供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)

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

© www.soinside.com 2019 - 2024. All rights reserved.