Как демаршалировать JSON с универсальным интерфейсом в качестве поля

У меня есть общий объект Response со следующей структурой:

type Response struct {
    Data          Data   `json:"data"`
    Error         string `json:"error,omitempty"`
    NextPageToken string `json:"next_page_token,omitempty"`
}

Тип Data — это интерфейс со многими реализациями (например, PingResponse и т. д.). Как мне преобразовать Response в его базовый тип? Ниже приведен полный пример, который всегда вызывает ошибку error: json: cannot unmarshal object into Go struct field Response.data of type main.Data :

type Response struct {
    Data          Data   `json:"data"`
    Error         string `json:"error,omitempty"`
    NextPageToken string `json:"next_page_token,omitempty"`
}

type Data interface{
    Foo()
}

type TypeA struct {
    Field1 string `json:"field1"`
    Field2 int    `json:"field2"`
}

func (a *TypeA) Foo() {}

type TypeB struct {
    Field3 float64 `json:"field3"`
}

func (b *TypeB) Foo() {}

func main() {
    jsonStr := `{
        "data": {
            "field1": "some string",
            "field2": 123
        },
        "error": "",
        "next_page_token": ""
    }`

    var response Response
    err := json.Unmarshal([]byte(jsonStr), &response)
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    switch data := response.Data.(type) {
    case *TypeA:
        fmt.Println("TypeA:", data.Field1, data.Field2)
    case *TypeB:
        fmt.Println("TypeB:", data.Field3)
    default:
        fmt.Println("Unknown type")
    }
}
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
1
0
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы должны сказать encoding/json, какой конкретный тип немаршалировать. Пакет не может сделать это за вас.

Предположим, что TypeA и TypeB определены как:

type TypeA struct {
    FieldA string `json:"field"`
}

type TypeB struct {
    FieldB string `json:"field"`
}

В этом случае невозможно решить, какой тип демаршалировать.

Что касается вашего примера, мы можем сказать encoding/json, какой тип неупорядочить, чтобы это понравилось:

- var response Response
+ response := Response{Data: &TypeA{}}

Если вы не знаете тип заранее, вы можете маршалировать его в map[string]interface{}:

 type Response struct {
-   Data          Data                   `json:"data"`
+   Data          map[string]interface{} `json:"data"`
    Error         string                 `json:"error,omitempty"` 
    NextPageToken string                 `json:"next_page_token,omitempty"`
 }

И определить тип следующим образом:

if field1, ok := response.Data["field1"]; ok {
    fmt.Println("TypeA:", field1, response.Data["field2"])
} else {
    if field3, ok := response.Data["field3"]; ok {
        fmt.Println("TypeB:", field3)
    } else {
        fmt.Println("Unknown type")
    }
}

Другое решение — встроить информацию о типе в json:

 jsonStr := `{
     "data": {
         "field1": "some string",
         "field2": 123
     },
+    type": "A",
     "error": "",
     "next_page_token": ""
 }`

 type Response struct {
-   Data          Data            `json:"data"`
+   Data          json.RawMessage `json:"data"`
+   Type          string          `json:"type"`
    Error         string          `json:"error,omitempty"`
    NextPageToken string          `json:"next_page_token,omitempty"`
 }

А затем расшифровать response.Data в соответствии со значением response.Type. См. пример, предоставленный encoding/json: https://pkg.go.dev/encoding/json#example-RawMessage-Unmarshal.

если вам нужно проверить данные, чтобы определить, как правильно их преобразовать, вам может пригодиться pkg.go.dev/github.com/mitchellh/mapstructure

erik258 30.03.2023 03:44

Другие вопросы по теме