Sto usando un'API che formatta le sue risposte in questo modo:
{
"err": 0,
"data": **Other json structure**
}
Il modo in cui sto ricevendo una risposta in questo momento è che sto inserendo la risposta in una struttura come questa:
type Response struct {
Err int `json:"err"`
Data interface{} `json:"data"`
}
e poi lo sto facendo dopo aver ricevuto una risposta
jsonbytes, _ := json.Marshal(resp.Data)
json.Unmarshal(jsonBytes, &dataStruct)
Sto solo ignorando gli errori per questo esempio.
Mi sembra un po 'strano che io stia facendo il marshalling e il unmarshaling quando so come dovrebbero essere i dati e che tipo dovrebbero essere.
C'è una soluzione più semplice che non vedo o è una cosa normale da fare?
Modifica: dovrei probabilmente menzionare che l'attributo Data nell'oggetto risposta può variare a seconda della chiamata API che sto facendo.
risposte:
2 per risposta № 1Il unmarshaller JSON utilizza la riflessione per esaminare il tipo a cui non sta eseguendo il marshalling. Dato un non inizializzato interface{}
come destinazione per i dati senza marshalling, un JSON object
viene smascherato in a map[string]interface{}
(esempio nel parco giochi).
Ecco alcune idee.
Se si conosce il tipo di dati, è possibile definire una nuova struttura di risposta per ciascun tipo. Esempio:
type FooResponse struct {
Err int `json:"err"`
Data Foo `json:"data"`
}
type Foo struct {
FooField string `json:"foofield"`
}
type BarResponse struct {
Err int `json:"err"`
Data Bar `json:"data"`
}
type Bar struct {
BarField string `json:"barfield"`
}
Opzione B
Se preferisci avere un singolo Response
struct invece di uno per tipo, puoi dire al unmarshaller JSON di evitare di smascherare il file data
fino a un momento successivo utilizzando il json.RawMessage
tipo di dati:
package main
import (
"encoding/json"
"fmt"
"log"
)
type Response struct {
Err int `json:"err"`
Data json.RawMessage `json:"data"`
}
type Foo struct {
FooField string `json:"foofield"`
}
type Bar struct {
BarField string `json:"barfield"`
}
func main() {
fooRespJSON := []byte(`{"data":{"foofield":"foo value"}}`)
barRespJSON := []byte(`{"data":{"barfield":"bar value"}}`)
var (
resp Response
foo Foo
bar Bar
)
// Foo
if err := json.Unmarshal(fooRespJSON, &resp); err != nil {
log.Fatal(err)
}
if err := json.Unmarshal(resp.Data, &foo); err != nil {
log.Fatal(err)
}
fmt.Println("foo:", foo)
// Bar
if err := json.Unmarshal(barRespJSON, &resp); err != nil {
log.Fatal(err)
}
if err := json.Unmarshal(resp.Data, &bar); err != nil {
log.Fatal(err)
}
fmt.Println("bar:", bar)
}
Produzione:
foo: {foo value}
bar: {bar value}
https://play.golang.org/p/Y7D4uhaC4a8
Opzione C
Una terza opzione, come sottolineato da @mkopriva in un commento sulla domanda, è di usare interface{}
come tipo di dati intermedio e preinizializzarlo su un tipo di dati noto.
L'enfasi si trova sulla parola intermediario - ovviamente passando per un interface{}
è meglio evitare (Go Piver "Proverbi). Il caso d'uso qui è quello di consentire l'utilizzo di qualsiasi tipo di dati senza la necessità di più diversi Response
tipi. Sulla strada per evitare di esporre interface{}
è avvolgere completamente la risposta, esponendo solo i dati e l'errore:
package main
import (
"encoding/json"
"fmt"
"log"
)
type Foo struct {
FooField string `json:"foofield"`
}
type Bar struct {
BarField string `json:"barfield"`
}
type Error struct {
Code int
}
func (e *Error) Error() string {
return fmt.Sprintf("error code %d", e.Code)
}
func unmarshalResponse(data []byte, v interface{}) error {
resp := struct {
Err int `json:"err"`
Data interface{} `json:"data"`
}{Data: v}
if err := json.Unmarshal(data, &resp); err != nil {
return err
}
if resp.Err != 0 {
return &Error{Code: resp.Err}
}
return nil
}
func main() {
fooRespJSON := []byte(`{"data":{"foofield":"foo value"}}`)
barRespJSON := []byte(`{"data":{"barfield":"bar value"}}`)
errRespJSON := []byte(`{"err": 123}`)
// Foo
var foo Foo
if err := unmarshalResponse(fooRespJSON, &foo); err != nil {
log.Fatal(err)
}
fmt.Println("foo:", foo)
// Bar
var bar Bar
if err := unmarshalResponse(barRespJSON, &bar); err != nil {
log.Fatal(err)
}
fmt.Println("bar:", bar)
// Error response
var v interface{}
if err := unmarshalResponse(errRespJSON, &v); err != nil {
log.Fatal(err)
}
}
Produzione:
foo: {foo value}
bar: {bar value}
2009/11/10 23:00:00 error code 123
https://play.golang.org/p/5SVfQGwS-Wy