在 Go 中解组 JSON 标记的联合
Unmarshal JSON tagged union in Go
我正在尝试整理 Google Actions 的 JSON 请求。这些有像这样的标记联合数组:
{
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"inputs": [{
"intent": "action.devices.QUERY",
"payload": {
"devices": [{
"id": "123",
"customData": {
"fooValue": 74,
"barValue": true,
"bazValue": "foo"
}
}, {
"id": "456",
"customData": {
"fooValue": 12,
"barValue": false,
"bazValue": "bar"
}
}]
}
}]
}
{
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"inputs": [{
"intent": "action.devices.EXECUTE",
"payload": {
"commands": [{
"devices": [{
"id": "123",
"customData": {
"fooValue": 74,
"barValue": true,
"bazValue": "sheepdip"
}
}, {
"id": "456",
"customData": {
"fooValue": 36,
"barValue": false,
"bazValue": "moarsheep"
}
}],
"execution": [{
"command": "action.devices.commands.OnOff",
"params": {
"on": true
}
}]
}]
}
}]
}
etc.
显然我可以将其解编为 interface{}
并使用完全动态类型转换和所有内容来解码它,但 Go 对解码为结构有很好的支持。有没有办法在 Go 中优雅地做到这一点(例如 like you can in Rust)?
我觉得你几乎可以通过最初阅读解组来做到这一点:
type Request struct {
RequestId string
Inputs []struct {
Intent string
Payload interface{}
}
}
但是一旦你有了 Payload interface{}
似乎没有任何方法可以将它反序列化为 struct
(除了序列化它并再次反序列化它很糟糕。有什么好的解决方案?
您可以将其存储为 json.RawMessage
,然后根据 Intent 的值将其解组,而不是将 Payload
解组为 interface{}
。 json 文档中的示例显示了这一点:
https://golang.org/pkg/encoding/json/#example_RawMessage_unmarshal
将该示例与您的 JSON 结合使用,您的代码结构将变成如下所示:
type Request struct {
RequestId string
Inputs []struct {
Intent string
Payload json.RawMessage
}
}
var request Request
err := json.Unmarshal(j, &request)
if err != nil {
log.Fatalln("error:", err)
}
for _, input := range request.Inputs {
var payload interface{}
switch input.Intent {
case "action.devices.EXECUTE":
payload = new(Execute)
case "action.devices.QUERY":
payload = new(Query)
}
err := json.Unmarshal(input.Payload, payload)
if err != nil {
log.Fatalln("error:", err)
}
// Do stuff with payload
}
我正在尝试整理 Google Actions 的 JSON 请求。这些有像这样的标记联合数组:
{
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"inputs": [{
"intent": "action.devices.QUERY",
"payload": {
"devices": [{
"id": "123",
"customData": {
"fooValue": 74,
"barValue": true,
"bazValue": "foo"
}
}, {
"id": "456",
"customData": {
"fooValue": 12,
"barValue": false,
"bazValue": "bar"
}
}]
}
}]
}
{
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"inputs": [{
"intent": "action.devices.EXECUTE",
"payload": {
"commands": [{
"devices": [{
"id": "123",
"customData": {
"fooValue": 74,
"barValue": true,
"bazValue": "sheepdip"
}
}, {
"id": "456",
"customData": {
"fooValue": 36,
"barValue": false,
"bazValue": "moarsheep"
}
}],
"execution": [{
"command": "action.devices.commands.OnOff",
"params": {
"on": true
}
}]
}]
}
}]
}
etc.
显然我可以将其解编为 interface{}
并使用完全动态类型转换和所有内容来解码它,但 Go 对解码为结构有很好的支持。有没有办法在 Go 中优雅地做到这一点(例如 like you can in Rust)?
我觉得你几乎可以通过最初阅读解组来做到这一点:
type Request struct {
RequestId string
Inputs []struct {
Intent string
Payload interface{}
}
}
但是一旦你有了 Payload interface{}
似乎没有任何方法可以将它反序列化为 struct
(除了序列化它并再次反序列化它很糟糕。有什么好的解决方案?
您可以将其存储为 json.RawMessage
,然后根据 Intent 的值将其解组,而不是将 Payload
解组为 interface{}
。 json 文档中的示例显示了这一点:
https://golang.org/pkg/encoding/json/#example_RawMessage_unmarshal
将该示例与您的 JSON 结合使用,您的代码结构将变成如下所示:
type Request struct {
RequestId string
Inputs []struct {
Intent string
Payload json.RawMessage
}
}
var request Request
err := json.Unmarshal(j, &request)
if err != nil {
log.Fatalln("error:", err)
}
for _, input := range request.Inputs {
var payload interface{}
switch input.Intent {
case "action.devices.EXECUTE":
payload = new(Execute)
case "action.devices.QUERY":
payload = new(Query)
}
err := json.Unmarshal(input.Payload, payload)
if err != nil {
log.Fatalln("error:", err)
}
// Do stuff with payload
}