在Go中解组顶级JSON数组

时间:2014-09-25 08:39:40

标签: arrays json go

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

通过对象响应,我可以用2行代码解释它:     structResult:= Foo {}     json.Unmarshal(structBody,& structResult)

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

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部分解决了我的问题,但是我希望在JSON具有不同结构的情况下我可以使用BaseResult来存储解析错误:

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

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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

只需在解组时指定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)
}