Unmarshal JSON помечен союз в Go - PullRequest
1 голос
/ 05 мая 2019

Я пытаюсь демаршализировать JSON-запросы Действия Google . У них есть массивы помеченных союзов, как это:

{
    "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 (, как, например, в Rust )?

Я чувствую, что вы могли бы почти сделать это, прочитав сначала демаршаллинг:

type Request struct {
    RequestId string
    Inputs    []struct {
        Intent   string
        Payload  interface{}
    }
}

Однако, как только у вас есть Payload interface{}, кажется, нет никакого способа десериализовать это в struct (кроме сериализации и повторной десериализации, что отстой. Есть ли какое-то хорошее решение?

1 Ответ

2 голосов
/ 05 мая 2019

Вместо демаршалирования Payload в interface{} вы можете сохранить его как json.RawMessage, а затем распаковать его в зависимости от значения Intent. Это показано в примере в документации 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
}
...