【问题标题】:Serialize a mixed type JSON array in Go在 Go 中序列化混合类型的 JSON 数组
【发布时间】:2015-03-16 22:25:48
【问题描述】:

我想返回一个如下所示的结构:

{
    results: [
        ["ooid1", 2.0, "Söme text"],
        ["ooid2", 1.3, "Åther text"],
    ]
}

这是一个由字符串、浮点数、unicode 字符组成的数组。

如果是 Python,我可以:

import json
json.dumps({'results': [["ooid1", 2.0, u"Söme text"], ...])

但在 Go 中,您不能拥有混合类型的数组(或切片)。

我想过使用这样的结构:

type Row struct {
    Ooid string
    Score float64
    Text rune
}

但我不希望每个都成为字典,我希望它成为每个包含 3 个元素的数组。

【问题讨论】:

  • 为什么你不想要一个结构?只是为了JSON?处理代码中其他任何地方的接口数组很烦人,因此我只会在(几乎)没有其他代码访问数据并且仅用于编组为 JSON 或其他内容的情况下使用它。您可以轻松编写自定义编组器,将您提供的结构写入 JSON 数组。

标签: arrays json go


【解决方案1】:

我们可以通过实现json.Marshaler 接口来自定义对象的序列化方式。对于我们的特殊情况,我们似乎有一个 Row 元素切片,我们希望将其编码为一组异构值。我们可以通过在 Row 类型上定义一个 MarshalJSON 函数来做到这一点,使用 interface{} 的中间切片来编码混合值。

This example 演示:

package main

import (
    "encoding/json"
    "fmt"
)

type Row struct {
    Ooid  string
    Score float64
    Text  string
}

func (r *Row) MarshalJSON() ([]byte, error) {
    arr := []interface{}{r.Ooid, r.Score, r.Text}
    return json.Marshal(arr)
}

func main() {
    rows := []Row{
        {"ooid1", 2.0, "Söme text"},
        {"ooid2", 1.3, "Åther text"},
    }
    marshalled, _ := json.Marshal(rows)
    fmt.Println(string(marshalled))
}

当然,我们也可能想反过来,从 JSON 字节返回结构。所以我们可以使用类似的json.Unmarshaler 接口。

func (r *Row) UnmarshalJSON(bs []byte) error {
    arr := []interface{}{}
    json.Unmarshal(bs, &arr)
    // TODO: add error handling here.
    r.Ooid = arr[0].(string)
    r.Score = arr[1].(float64)
    r.Text = arr[2].(string)
    return nil
}

这使用了类似的技巧,首先使用interface{} 的中间切片,使用解组器将值放入这个通用容器中,然后将值放回我们的结构中。

package main

import (
    "encoding/json"
    "fmt"
)

type Row struct {
    Ooid  string
    Score float64
    Text  string
}

func (r *Row) UnmarshalJSON(bs []byte) error {
    arr := []interface{}{}
    json.Unmarshal(bs, &arr)
    // TODO: add error handling here.
    r.Ooid = arr[0].(string)
    r.Score = arr[1].(float64)
    r.Text = arr[2].(string)
    return nil
}

func main() {
    rows := []Row{}
    text := `
    [
          ["ooid4", 3.1415, "pi"],
          ["ooid5", 2.7182, "euler"]
        ]
    `
    json.Unmarshal([]byte(text), &rows)
    fmt.Println(rows)
}

您可以阅读完整示例here

【讨论】:

    【解决方案2】:

    使用[]interface{}

    type Results struct {
         Rows []interface{} `json:"results"`
    }
    

    如果要访问存储在[]interface{} 中的值,则必须使用类型断言

    for _, row := range results.Rows {
        switch r := row.(type) {
        case string:
            fmt.Println("string", r)
        case float64:
            fmt.Println("float64", r)
        case int64:
            fmt.Println("int64", r)
        default:
            fmt.Println("not found")
        } 
    }
    

    【讨论】:

      【解决方案3】:

      有些笨拙,但你可以

      type result [][]interface{}
      type results struct {
          Results result
      }
      

      工作示例https://play.golang.org/p/IXAzZZ3Dg7

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-08-16
        • 2018-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多