在Go中解组顶级JSON数组

I'm learning Go by writing a simple http server and I need to handle some JSON responses.

With an object response, I can unmarshal it idiomatically with 2 lines of code: structResult := Foo{} json.Unmarshal(structBody, &structResult)

I don't know how to do the same for an array response (see the example below). Is there a way to specify (possibly via json tag) that top-level array should go into a given struct field?

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
", 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
", arrayResult)
}

I know I could make FooResult an array:

type FooResult []Foo

but then I lose the ability to specify base object which I would like to use to store error message and such. I also know that I can unmarshal into &fooResult.Foos directly, but I want the code to work with both objects and arrays.

UPDATE

Implementing UnmarshalJSON as suggested by @dyoo partially solves my problem, but I was hoping that I could use BaseResult to store parse error in case JSON has a different structure:

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

Of course I could implement more complex logic inside UnmarshalJSON - but isn't there a simpler way to do it?

douzhi3586
douzhi3586 当然,在此示例中,我仅使用名称“foo”。
大约 6 年之前 回复
dsavz66262
dsavz66262 建议:将FooResult重命名为ArrayResult。将Foo用作名称的任何部分通常是可疑的,因为它没有任何意义。
大约 6 年之前 回复

2个回答

You can implement the json.Unmarshaler interface in your FooResult, to customize exactly how it responds to unmarshaling. (Similarly, there's a json.Marshaler interface.)

Add:

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

after which your code should otherwise work. http://play.golang.org/p/oMdoB2e-rB

You might try something like:

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
}

although even this is beginning to look dubious. Handling union type results is not quite what the json library is designed to handle automatically for you. You'll need to explicitly code the coercion logic if your JSON has dynamic type.

See: How to unmarshall an array of different types correctly? and http://blog.golang.org/json-and-go for related issues.

dongyata3336
dongyata3336 您无需实现UnmarshalJSON,只需在解组时指定Foos。 我在下面的答案中有一个例子。
大约 6 年之前 回复
dtxooq1020
dtxooq1020 谢谢。 这回答了我的问题,但真正引起我问题的原因是,我希望使用基本结构来存储错误,以防返回的JSON具有不同的结构(许多API用这种方式封装)。 我想知道是否有可能。
大约 6 年之前 回复

Just specify Foos when you Unmarshal

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
", 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
", arrayResult)
}
dongmu5246
dongmu5246 谢谢。 我知道我可以做到,但是我的主要目的是使解析逻辑保持与struct接近,并避免为每种情况编写代码。 通过UnmarshalJSON的@dyoo解决方案更好。
大约 6 年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问