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

huangapple go评论78阅读模式
英文:

Best way to represent this JSON in Go?

问题

我正在编写一个用于返回Geckoboard数据的端点,它期望的格式如下:

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

"item" 是一个包含不同结构的数组。我该如何在Go中表示这个数据?

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

英文:

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

{
  "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?

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

答案1

得分: 1

这个东西比你想象的要简单。只是对于普通读者来说,文档不是很完善。我建议使用ffjson而不是普通的json。它的设计方式使得你在除了库名之外不需要改变语法。

使用方法很简单:

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)

结构体的缺点(即使在ffjson中仍然存在)是始终会使用reflection,这在需要高性能的情况下会浪费大量的CPU周期。当你将其限制在映射中时,ffjson比普通的json快2-3倍。这样,该库可以编译每个你编组的数据结构并重复使用它,而不是不断使用reflect检查数据完整性/结构。

英文:

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.

It's easy as this:

type User struct {
	Id      int    `json:&#39;id&#39;`
	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 &lt; 10; i++ {
	item[strconv.itoa(i)] = User{i, &quot;Username X&quot;, 38393, 29384, 12393, 123981}
}
buf, err := ffjson.Marshal(&amp;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.

答案2

得分: 1

有一种简单的方法可以创建某个数据结构的值,你知道你想要生成/复制的JSON输出:

将你想要生成的输出解组成map[string]interface{}。你将得到一个map值,当你进行编组时,将得到你想要的输出。当你解组预期的输出时,你可以检查结果map值,以了解你需要创建什么才能得到你期望的输出。

这在你的情况下也适用。以下是代码:

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

其中input是你的JSON输入:

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

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

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

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

分析你解组的map值:

显然它有一个键"item",它的值的类型是:

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

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

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

所以这个切片包含2个值:

  • 一个map("value" : "274057"对)
  • 和另一个切片(id或数字的列表)

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

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

对此进行编组将产生相同的JSON输出。

英文:

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

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.

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

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

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

Where input is your JSON input:

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

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

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

Try the complete application on the Go Playground.

Analyzing your unmarshaled map value:

Obviously it has a key &quot;item&quot;, its value's type:

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

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

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

So the slice contains 2 values:

  • a map (the &quot;value&quot; : &quot;274057&quot; pair)
  • and another slice (the list of ids or numbers)

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

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

Marshaling this produces the same JSON output.

huangapple
  • 本文由 发表于 2015年5月13日 19:56:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/30214094.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定