在Go中表示这个JSON的最佳方式?

时间:2022-04-21 09:13:27

I am writing an endpoint to return data for Geckoboard, it excepts a format like so:

我正在编写一个端点来返回Geckoboard的数据,它除了这样的格式:

{
  "item": [
    {
      "value": "274057"
    },
    [
      "38594",
      "39957",
      "35316",
      "35913",
      "36668",
      "45660",
      "41949"
    ]
  ]
}

"item" is an array of varying structures. How would I represent this data in Go?

“item”是一组不同的结构。我如何在Go中表示这些数据?

Note: this is not on how I would unmarshal this, I need to generate this format.

注意:这不是关于如何解组这个,我需要生成这种格式。

2 个解决方案

#1


This stuff is easier than you might think. It's just not that well documented for the casual reader. I would recommend ffjson over normal json tho. It's made up in such a manner that you don't need to change your syntax other than the library name.

这个东西比你想象的要容易。这对于休闲读者而言并不是很好。我建议ffjson超过正常的json tho。它的构成方式使您无需更改库名称以外的语法。

It's easy as this:

这很容易:

type User struct {
    Id      int    `json:'id'`
    Name    string `json:name`
    SomeId1 int    `json:some_id_1`
    SomeId2 int    `json:some_id_2`
    SomeId3 int    `json:some_id_3`
    SomeId4 int    `json:some_id_4`
}

item := map[string]User{}
for i := 0; i < 10; i++ {
    item[strconv.itoa(i)] = User{i, "Username X", 38393, 29384, 12393, 123981}
}
buf, err := ffjson.Marshal(&item)

The downside of structs (even in ffjson still) is that reflection will always be used, which, in times that you're in need of high performance, you will waste a lot of CPU cycles. ffjson is 2-3 times faster than normal json when you keep it to maps. This way the library can compile every data-structure you marshal and re-use it instead of constantly inspecting the data-integrity/structure with reflect.

结构的缺点(即使在ffjson中)仍然会使用反射,在你需要高性能的时候,你将浪费大量的CPU周期。当你将它保存到地图时,ffjson比普通的json快2-3倍。通过这种方式,库可以编译您编组的每个数据结构并重新使用它,而不是通过反射不断地检查数据完整性/结构。

#2


There is an easy way to create a value of some data structure where you know the JSON output you want to generate/duplicate:

有一种简单的方法可以创建一些数据结构的值,您可以在其中知道要生成/复制的JSON输出:

Take the output you want to generate, and unmarshal it into a map[string]interface{}. You will get a map value which when you marshal will result in your desired output. When you unmarshal an expected output, you can inspect the result map value to know what you need to create in order for your expected output.

获取要生成的输出,并将其解组为map [string] interface {}。您将获得一个地图值,当您编组时将产生您想要的输出。解组预期输出时,可以检查结果映射值以了解需要为预期输出创建的内容。

This works in your case too. Here is the code:

这也适用于您的情况。这是代码:

var m map[string]interface{}
err := json.Unmarshal([]byte(input), &m)
if err != nil {
    panic(err)
}
fmt.Printf("%+v\n", m)

res, err := json.MarshalIndent(m, "", "  ")
if err != nil {
    panic(err)
}
fmt.Println(string(res))

Where input is your JSON input:

输入是您的JSON输入:

const input = `{
  "item": [
    {
      "value": "274057"
    },
    [
      "38594",
      "39957",
      "35316",
      "35913",
      "36668",
      "45660",
      "41949"
    ]
  ]
}`

This program generates the same output as your required output (or input):

该程序生成与所需输出(或输入)相同的输出:

map[item:[map[value:274057] [38594 39957 35316 35913 36668 45660 41949]]]
{
  "item": [
    {
      "value": "274057"
    },
    [
      "38594",
      "39957",
      "35316",
      "35913",
      "36668",
      "45660",
      "41949"
    ]
  ]
}

Try the complete application on the Go Playground.

在Go Playground上试用完整的应用程序。

Analyzing your unmarshaled map value:

Obviously it has a key "item", its value's type:

显然它有一个关键的“项目”,它的值类型:

fmt.Printf("%T\n", m["item"]); // Prints []interface{}

So it's a slice. It has 2 values, their types:

所以这是一个切片。它有2个值,它们的类型:

fmt.Printf("%T\n", m["item"].([]interface{})[0]); // map[string]interface{}
fmt.Printf("%T\n", m["item"].([]interface{})[1]); // []interface{}

So the slice contains 2 values:

因此切片包含2个值:

  • a map (the "value" : "274057" pair)
  • 地图(“值”:“274057”对)

  • and another slice (the list of ids or numbers)
  • 和另一个切片(ids或数字列表)

Here is the Go code to reproduce the same map value:

以下是重现相同地图值的Go代码:

m := map[string]interface{}{
    "item": []interface{}{
        map[string]interface{}{"value": "274057"},
        []interface{}{"38594", "39957", "35316", "35913", "36668", "45660", "41949"},
    },
}

Marshaling this produces the same JSON output.

对其进行编组会生成相同的JSON输出。

#1


This stuff is easier than you might think. It's just not that well documented for the casual reader. I would recommend ffjson over normal json tho. It's made up in such a manner that you don't need to change your syntax other than the library name.

这个东西比你想象的要容易。这对于休闲读者而言并不是很好。我建议ffjson超过正常的json tho。它的构成方式使您无需更改库名称以外的语法。

It's easy as this:

这很容易:

type User struct {
    Id      int    `json:'id'`
    Name    string `json:name`
    SomeId1 int    `json:some_id_1`
    SomeId2 int    `json:some_id_2`
    SomeId3 int    `json:some_id_3`
    SomeId4 int    `json:some_id_4`
}

item := map[string]User{}
for i := 0; i < 10; i++ {
    item[strconv.itoa(i)] = User{i, "Username X", 38393, 29384, 12393, 123981}
}
buf, err := ffjson.Marshal(&item)

The downside of structs (even in ffjson still) is that reflection will always be used, which, in times that you're in need of high performance, you will waste a lot of CPU cycles. ffjson is 2-3 times faster than normal json when you keep it to maps. This way the library can compile every data-structure you marshal and re-use it instead of constantly inspecting the data-integrity/structure with reflect.

结构的缺点(即使在ffjson中)仍然会使用反射,在你需要高性能的时候,你将浪费大量的CPU周期。当你将它保存到地图时,ffjson比普通的json快2-3倍。通过这种方式,库可以编译您编组的每个数据结构并重新使用它,而不是通过反射不断地检查数据完整性/结构。

#2


There is an easy way to create a value of some data structure where you know the JSON output you want to generate/duplicate:

有一种简单的方法可以创建一些数据结构的值,您可以在其中知道要生成/复制的JSON输出:

Take the output you want to generate, and unmarshal it into a map[string]interface{}. You will get a map value which when you marshal will result in your desired output. When you unmarshal an expected output, you can inspect the result map value to know what you need to create in order for your expected output.

获取要生成的输出,并将其解组为map [string] interface {}。您将获得一个地图值,当您编组时将产生您想要的输出。解组预期输出时,可以检查结果映射值以了解需要为预期输出创建的内容。

This works in your case too. Here is the code:

这也适用于您的情况。这是代码:

var m map[string]interface{}
err := json.Unmarshal([]byte(input), &m)
if err != nil {
    panic(err)
}
fmt.Printf("%+v\n", m)

res, err := json.MarshalIndent(m, "", "  ")
if err != nil {
    panic(err)
}
fmt.Println(string(res))

Where input is your JSON input:

输入是您的JSON输入:

const input = `{
  "item": [
    {
      "value": "274057"
    },
    [
      "38594",
      "39957",
      "35316",
      "35913",
      "36668",
      "45660",
      "41949"
    ]
  ]
}`

This program generates the same output as your required output (or input):

该程序生成与所需输出(或输入)相同的输出:

map[item:[map[value:274057] [38594 39957 35316 35913 36668 45660 41949]]]
{
  "item": [
    {
      "value": "274057"
    },
    [
      "38594",
      "39957",
      "35316",
      "35913",
      "36668",
      "45660",
      "41949"
    ]
  ]
}

Try the complete application on the Go Playground.

在Go Playground上试用完整的应用程序。

Analyzing your unmarshaled map value:

Obviously it has a key "item", its value's type:

显然它有一个关键的“项目”,它的值类型:

fmt.Printf("%T\n", m["item"]); // Prints []interface{}

So it's a slice. It has 2 values, their types:

所以这是一个切片。它有2个值,它们的类型:

fmt.Printf("%T\n", m["item"].([]interface{})[0]); // map[string]interface{}
fmt.Printf("%T\n", m["item"].([]interface{})[1]); // []interface{}

So the slice contains 2 values:

因此切片包含2个值:

  • a map (the "value" : "274057" pair)
  • 地图(“值”:“274057”对)

  • and another slice (the list of ids or numbers)
  • 和另一个切片(ids或数字列表)

Here is the Go code to reproduce the same map value:

以下是重现相同地图值的Go代码:

m := map[string]interface{}{
    "item": []interface{}{
        map[string]interface{}{"value": "274057"},
        []interface{}{"38594", "39957", "35316", "35913", "36668", "45660", "41949"},
    },
}

Marshaling this produces the same JSON output.

对其进行编组会生成相同的JSON输出。