如何在 Go 中将 JSON 对象数组转换为具有默认值的结构数组?
Posted
技术标签:
【中文标题】如何在 Go 中将 JSON 对象数组转换为具有默认值的结构数组?【英文标题】:How do I turn an array of JSON objects into an array of structs with default values in Go? 【发布时间】:2016-01-08 03:00:14 【问题描述】:我正在开发一个可以接收由 JSON 对象数组组成的 POST 的 Go API。 POST 的结构类似于:
[
"name":"Las Vegas",
"size":14
,
"valid": false,
"name":"Buffalo",
"size":63
]
假设我有以下结构:
type Data
Valid bool
Name string
Size float64
我想创建一组Data
s,并将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 个字段,其中一些具有我想要分配默认值的值,其他的可以留空,但所有这些都必须通过使用这种方法进行迭代。
有没有更好的办法?
【问题讨论】:
【参考方案1】:您可以使用json.RawMessage
类型来延迟解组某些 JSON 文本值。如果您使用此类型,则 JSON 文本将存储在此中而无需解组(因此您可以稍后根据需要解组此片段)。
因此,在您的情况下,如果您尝试解组为此类RawMessage
的切片,则可以使用您在问题中链接的技术,即您可以遍历原始值切片(即 JSON 文本对于每个Data
),创建一个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] = DataValid: 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] = &DataValid: 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)
【讨论】:
工作出色,谢谢。这可能是一个幼稚的问题,指针切片与结构切片相比哪个更有效?无论哪种方式,代码都在使用引用,不是吗? @jboschiero 在指针的情况下,当您为切片元素赋值时,它只复制一个指针(4 或 8 个字节)。如果切片包含非指针,则为切片元素(例如allData[i] = &DataValid: true
)分配一个值会复制整个结构,如果你的结构很大(你说它有14个字段)可能很大,所以如果结构的大小是200字节,分配复制 200 个字节。【参考方案2】:
您可以通过在您的类型上提供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
)并让它按照您想要的方式工作。
【讨论】:
以上是关于如何在 Go 中将 JSON 对象数组转换为具有默认值的结构数组?的主要内容,如果未能解决你的问题,请参考以下文章
如何在android中将json对象从json数组转换为字符串数组
如何在 Javascript 中将表单数组转换为 JSON? (对象数组)?