将interface{}从JSON转换为多个具体类型之一

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

Convert interface{} from JSON to one of multiple concrete types

问题

我一直在寻找解决这个问题的方法,在这里没有找到。我不确定是否可能,但还是值得问一下。

我有一个接口,我的应用程序从服务器读取JSON数据。可能会返回几种类型,所以我使用interface{}来消费它。

在将JSON字节解组为interface{}之后,我如何将其转换为具体类型?如果失败,我希望检查下一个类型进行转换,直到成功转换为止。

大致代码如下(为了简洁起见,删除了一些代码):

type Fooer interface {
    Foo()
}

type A struct { }

func (a *A) Foo() { .. }

type B struct { }

func (b *B) Foo() { .. }

// resp is io.ReadCloser
func heavvyWork(resp) {
    var parsedResp interface{}
    bytesData, err := ioutil.ReadAll(resp)
    json.Unmarshal(bytesData, parsedResp)

    // 将parsedResp转换为具体类型,可能是A或B结构体
    // 尝试A,如果失败,则尝试B。
    ...
    concreteType.Foo()
}

我已经查看了类型断言和类型转换,但无法使其工作。

在Go中是否可能实现这个功能?

JSON响应内容的示例:

A:
{
  "data": {
    "access_key": "AKIA...",
    "secret_key": "xlCs...",
  }
}

B:
{
  "data": {
    "data": {
      "foo": "bar"
    },
    "metadata": {
      ...
    }
  }
}
英文:

I've been looking around for a solution to this problem, and couldn't find one here on SO. I'm not sure it's possible, but its worth asking.

I have an interface, and my app reads JSON data from a server. There are few types that might return in response, so I'm using interface{} to consume it.

After I've unmarshaled the json bytes to an interface{}, how can I convert it to a concrete type? If it fails, I would like to check the next type for conversation, until I have a successful conversation.

It looks something like this (some code is removed for brevity)

type Fooer interface {
    Foo()
}

type A struct { }

func (a *A) Foo() { .. }

type B struct { }

func (b *B) Foo() { .. }

// resp is io.ReadCloser
func heavvyWork(resp) {
    var parsedResp interface{}
    bytesData, err := ioutil.ReadAll(resp)
    json.Unmarshal(bytesData, parsedResp)

    // convert parsedResp to a concrete type, it might be A or B struct
    // try A, if fails, try B.
    ...
    concreteType.Foo()
}

I have looked over type assertions and casting, but couldn't get this to work.

Is that possible with Go?

Example for JSON response content

A:
{
  "data": {
    "access_key": "AKIA...",
    "secret_key": "xlCs...",
  }
}

B:
{
  "data": {
    "data": {
      "foo": "bar"
    },
    "metadata": {
      ...
    }
  }
}

答案1

得分: 1

你有几个选择,但没有一个是完全理想的。

首先,你可以直接在地图中查找键。我在这里写了一个示例,点击这里。这个示例旨在提供指导,但并不特别高效或其他方面。

其次,你可以使用json.RawMessage来延迟解析。我在这里写了一个示例,点击这里。请注意,我在解码器中使用了各种结构体的指针成员。这会让事情变得麻烦:你可能希望在解码时使用指针化的数据类型,并在选择了类型后使用非指针类型。但这样做可以让你直接使用更多的json包功能(通过结构体标签)。

另一种方法是使用json.NewDecoder获取流解码器并进行处理。这可能是最健壮的方法,但也是最棘手的方法。

英文:

You have options. None of them are exactly wonderful.

One is, as mentioned, to just look inside the map for keys. I wrote up an example of this here. It's meant to be instructive, but not particularly efficient or anything.

Another is to use json.RawMessage to defer parsing a bit. I wrote up an example of this here. Note how I resort to pointer members of the various structs in the decoder. This makes things annoying: you might want to have pointer-ized data types just for the decode, and use non-pointer types once you've picked a type. But it does let you use more of the json package directly (via struct tags).

Yet another is to use json.NewDecoder to get a stream decoder and work through it. This would probably be the most robust method, but also the trickiest.

huangapple
  • 本文由 发表于 2021年5月26日 14:49:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/67699868.html
匿名

发表评论

匿名网友

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

确定