Golang демаршалинг JSON в структуры, сгенерированные protobuf

Я хотел бы получить ответ JSON с клиентским приложением и демаршалировать этот ответ в структуру. Чтобы гарантировать, что структура остается неизменной во всех клиентских приложениях, использующих этот пакет, я хотел бы определить ответы JSON как сообщения protobuf. У меня возникают трудности с демаршалингом JSON в структуры, созданные protobuf.

У меня есть следующие данные JSON:

[
  {
    "name": "C1",
    "type": "docker"
  },
  {
    "name": "C2",
    "type": "docker"
  }
]

Я смоделировал свои определения protobuf следующим образом:

syntax = "proto3";
package main;

message Container {
    string name = 1;
    string type = 2;
}

message Containers {
    repeated Container containers = 1;
}

Использование этого шаблона со структурами нормально работает, но по какой-то причине использование этих прото-определений вызывает проблемы. Приведенный ниже код демонстрирует рабочий и нерабочий пример. Хотя одна из версий работает, я не могу использовать это решение, поскольку []*Container не удовлетворяет proto.Message интерфейсу.

package main

import (
    "encoding/json"
    "fmt"
    "strings"

    "github.com/gogo/protobuf/jsonpb"
)

func working(data string) ([]*Container, error) {
    var cs []*Container
    return cs, json.Unmarshal([]byte(data), &cs)
}

func notWorking(data string) (*Containers, error) {
    c := &Containers{}
    jsm := jsonpb.Unmarshaler{}
    if err := jsm.Unmarshal(strings.NewReader(data), c); err != nil {
        return nil, err
    }
    return c, nil
}

func main() {
    data := `
[
  {
    "name": "C1",
    "type": "docker"
  },
  {
    "name": "C2",
    "type": "docker"
  }
]`

    w, err := working(data)
    if err != nil {
        panic(err)
    }
    fmt.Print(w)

    nw, err := notWorking(data)
    if err != nil {
        panic(err)
    }
    fmt.Print(nw.Containers)
}

Выполнение этого дает следующий результат:

[name:"C1" type:"docker"  name:"C2" type:"docker" ]
panic: json: cannot unmarshal array into Go value of type map[string]json.RawMessage

goroutine 1 [running]:
main.main()
        /Users/example/go/src/github.com/example/example/main.go:46 +0x1ee

Process finished with exit code 2

Есть ли способ демаршалировать этот JSON в Containers? Или, как вариант, сделать []*Container так, чтобы он удовлетворял интерфейсу proto.Message?


person rhillhouse    schedule 09.09.2020    source источник


Ответы (2)


Вы должны использовать NewDecoder для передачи данных в jsonDecoder, а затем пройти по массиву. Код следующий

 func main() {
        data := `
    [
      {
        "name": "C1",
        "type": "docker"
      },
      {
        "name": "C2",
        "type": "docker"
      }
    ]`
        jsonDecoder := json.NewDecoder(strings.NewReader(data))
        _, err := jsonDecoder.Token()
        if err != nil {
            log.Fatal(err)
        }
        var protoMessages []*pb.Container
        for jsonDecoder.More() {
            protoMessage := pb.Container{}
            err := jsonpb.UnmarshalNext(jsonDecoder, &protoMessage)
            if err != nil {
                log.Fatal(err)
            }
            protoMessages = append(protoMessages, &protoMessage)
        }
        fmt.Println("%s", protoMessages)
    }
person sk l    schedule 09.09.2020

Для сообщений Контейнеры, т.е.

message Containers {
    repeated Container containers = 1;
}

Правильный JSON должен выглядеть так:

{
   "containers" : [
      {
        "name": "C1",
        "type": "docker"
      },
      {
        "name": "C2",
        "type": "docker"
      }
    ]
}

Если вы не можете изменить JSON, вы можете использовать созданную вами функцию.

func working(data string) ([]*Container, error) {
    var cs []*Container
    err := json.Unmarshal([]byte(data), &cs)
    // handle the error here
    return &Containers{
       containers: cs,
    }, nil
}

person Obi Wan - PallavJha    schedule 09.09.2020