【问题标题】:Unmarshalling top-level JSON array in Go在 Go 中解组顶级 JSON 数组
【发布时间】:2014-09-25 08:39:39
【问题描述】:

我正在通过编写一个简单的 http 服务器来学习 Go,我需要处理一些 JSON 响应。

对于一个对象响应,我可以用 2 行代码习惯性地解组它: structResult := Foo{} json.Unmarshal(structBody, &structResult)

我不知道如何对数组响应做同样的事情(见下面的例子)。有没有办法指定(可能通过 json 标签)顶级数组应该进入给定的结构字段?

package main

import "fmt"
import "encoding/json"

type Foo struct {
    Id uint64 `json:"id"`
    Name string `json:"name"`
}

type BaseResult struct {
    Error string  `json:"error"`
}

type FooResult struct {
    BaseResult
    Foos []Foo
}

func main() {
    // Simple and works.
    structBody := []byte(`{"id": 1,"name": "foo"}`)
    structResult := Foo{}
    json.Unmarshal(structBody, &structResult)
    fmt.Printf("%#v\n", structResult)

    // Doesn't work.
    arrayBody := []byte(`[{"id": 1,"name": "foo"},{"id": 2,"name": "bar"},{"id": 3,"name": "foobar"}]`) 
    arrayResult := FooResult{}
    json.Unmarshal(arrayBody, &arrayResult)
    fmt.Printf("%#v\n", arrayResult)
}

我知道我可以让 FooResult 成为一个数组:

type FooResult []Foo

但是我失去了指定我想用来存储错误消息等的基础对象的能力。我也知道我可以直接解组为 &fooResult.Foos,但我希望代码可以同时处理对象和数组。

更新

按照@dyoo 的建议实施 UnmarshalJSON 部分解决了我的问题,但我希望我可以使用 BaseResult 来存储解析错误,以防 JSON 具有不同的结构:

arrayBody := []byte(`{"error": "foo"}`)
arrayResult := FooResult{}
json.Unmarshal(arrayBody, &arrayResult)
fmt.Printf("%#v\n", arrayResult)

当然,我可以在 UnmarshalJSON 中实现更复杂的逻辑 - 但没有更简单的方法吗?

【问题讨论】:

  • 建议:将FooResult 重命名为ArrayResult。使用Foo 作为名称的任何部分通常是可疑的,因为它没有任何意义。
  • 当然,我在这个例子中只使用了“foo”这个名字。

标签: arrays json go


【解决方案1】:

您可以在FooResult 中实现json.Unmarshaler 接口,以准确自定义它对解组的响应方式。 (类似地,还有一个json.Marshaler 接口。)

添加:

func (f *FooResult) UnmarshalJSON(bs []byte) error {
    return json.Unmarshal(bs, &f.Foos)
}

之后您的代码应该可以正常工作。 http://play.golang.org/p/oMdoB2e-rB

你可以试试这样的:

func (f *FooResult) UnmarshalJSON(bs []byte) error {
    err1 := json.Unmarshal(bs, &f.BaseResult)
    err2 := json.Unmarshal(bs, &f.Foos)
    if err1 != nil && err2 != nil {
        // Arbitrarily choose an error.
        return err1
    }
    return nil
}

尽管这已经开始令人怀疑了。处理 联合类型 结果并不是 json 库旨在为您自动处理的内容。如果您的 JSON 具有动态类型,则需要显式编码强制逻辑。

有关相关问题,请参阅:How to unmarshall an array of different types correctly?http://blog.golang.org/json-and-go

【讨论】:

  • 谢谢。这回答了我的问题,但我提出问题的真正原因是我希望使用基本结构来存储错误,以防返回的 JSON 具有不同的结构(许多 API 都以这种方式封装)。我想知道这是否可能。
  • 不需要实现UnmarshalJSON,解组时指定Foos即可。我在下面的回答中有一个例子。
【解决方案2】:

解组时只需指定Foos

package main

import "fmt"
import "encoding/json"

type Foo struct {
    Id   uint64 `json:"id"`
    Name string `json:"name"`
}

type BaseResult struct {
    Error string `json:"error"`
}

type FooResult struct {
    BaseResult
    Foos []Foo
}

func main() {
    // Simple and works.
    structBody := []byte(`{"id": 1,"name": "foo"}`)
    structResult := Foo{}
    json.Unmarshal(structBody, &structResult)
    fmt.Printf("%#v\n", structResult)

    // Doesn't work.
    arrayBody := []byte(`[{"id": 1,"name": "foo"},{"id": 2,"name": "bar"},{"id": 3,"name": "foobar"}]`)
    arrayResult := FooResult{}
    if err := json.Unmarshal(arrayBody, &arrayResult.Foos); err != nil {
        arrayResult.BaseResult.Error = string(arrayBody)
    }

    fmt.Printf("%#v\n", arrayResult)
}

【讨论】:

  • 谢谢。我知道我可以做到这一点,但我的重点是保持解析逻辑接近 struct 并避免为每种不同的情况编写代码。 @dyoo 通过 UnmarshalJSON 的解决方案更好。
猜你喜欢
  • 2017-07-11
  • 1970-01-01
  • 1970-01-01
  • 2016-12-16
  • 2020-10-07
  • 1970-01-01
  • 2023-01-13
  • 1970-01-01
  • 2020-08-05
相关资源
最近更新 更多