2017-06-27 5 views
2

Je veux écrire une fonction qui reçoit plusieurs types de structures et les dissocie de JSON. À cette fin, j'ai un autre ensemble de fonctions avec une signature prédéfinie qui renvoie les instances de struct mais comme chaque fonction renvoie un type de structure différent, la signature de la fonction a le type de retour interface{}. Quand j'envoie json.Unmarshal une structure concrète fonctionne comme je m'y attendais mais quand j'envoie la même structure que interface{}, elle le convertit en une carte.Comment dire à json.Unmarshal d'utiliser struct à la place de l'interface

Voici un code d'exemple simplifié qui représente le problème:

package main 

import (
"encoding/json" 
    "fmt" 
) 

type Foo struct { 
    Bar string `json:"bar"` 
} 

func getFoo() interface{} { 
    return Foo{"bar"} 
} 

func main() { 

    fooInterface := getFoo() 
    fooStruct := Foo{"bar"} 
    fmt.Println(fooInterface) //{bar} 
    fmt.Println(fooStruct) //{bar} 

    myJSON := `{"bar":"This is the new value of bar"}` 
    jsonBytes := []byte(myJSON) 

    err := json.Unmarshal(jsonBytes, &fooInterface) 
    if err != nil { 
     fmt.Println(err) 
    } 
    fmt.Println(fooInterface) //map[bar:This is the new value of bar] 

    err = json.Unmarshal(jsonBytes, &fooStruct) 
    if err != nil { 
     fmt.Println(err) 
    } 
    fmt.Println(fooStruct) //{This is the new value of bar} 
} 

https://play.golang.org/p/tOO7Ki_i4c

Je pensais json.Unmarshal utiliser la structure en béton derrière l'interface pour unmarshaling mais il n'a pas seulement et ayants droit la carte des valeurs à l'interface passée.

Pourquoi n'utilise-t-il pas la structure concrète et existe-t-il un moyen de lui dire d'utiliser le type de structure béton sans cast explicite (je ne connais pas le type explicite au moment du design)?

Répondre

3

Le paquet encoding/json ne peut pas deviner de façon magique le type de résultat auquel vous voulez donner le résultat, sauf si vous le lui demandez.

Une façon de dire à quoi unmarsal est de transmettre la valeur de ce type à la fonction json.Unmarshal().

Et malheureusement, il n'y a pas d'autre moyen. Si vous transmettez une valeur de type interface{}, l'implémentation du package json est libre de choisir un type de son choix et il choisit map[string]interface{} pour les objets JSON et []interface{} pour les tableaux JSON. Ceci est documenté à json.Unmarshal():

Pour JSON unmarshal en une valeur d'interface, les magasins unmarshal un de ceux-ci dans la valeur de l'interface:

bool, for JSON booleans 
float64, for JSON numbers 
string, for JSON strings 
[]interface{}, for JSON arrays 
map[string]interface{}, for JSON objects 
nil for JSON null 

Si vous connaissez le type d'avance, créez une valeur de ce type, et passer cela pour unmarshaling. Si votre magasin dans une variable interface{} auparavant n'a pas d'importance; Si la valeur passée est appropriée pour unmarshaling, elle sera utilisée. Notez que la valeur passée sera enveloppée dans un interface{} s'il n'est pas déjà de ce type, car il s'agit du type de paramètre json.Unmarshal().

Le problème de l'échec de votre code est dû au fait que vous transmettez une valeur de type *interface{} qui enveloppe une valeur non-pointeur Foo. Comme le package json ne peut pas l'utiliser, il crée une nouvelle valeur de son choix (une carte).

Au lieu de cela, vous devriez envelopper une valeur *Foo dans un interface{} et passer que:

func getFoo() interface{} { 
    return &Foo{"bar"} 
} 

func main() { 
    fooInterface := getFoo() 

    myJSON := `{"bar":"This is the new value of bar"}` 
    jsonBytes := []byte(myJSON) 

    err := json.Unmarshal(jsonBytes, fooInterface) 
    if err != nil { 
     fmt.Println(err) 
    } 
    fmt.Printf("%T %+v", fooInterface, fooInterface) 
} 

Il en résulte (essayer sur le Go Playground):

*main.Foo &{Bar:This is the new value of bar} 
+0

Voulez-vous dire que je ne peux pas extraire le type concret d'une interface (comme encoding/json ne peut pas le faire non plus)? – Buzzy

+0

@Buzzy Oui, vous pouvez. Mais vous avez passé '* interface {}'. Voir la réponse éditée. – icza

+0

Merci! Votre explication était claire et utile – Buzzy