J'ai une API qui renvoie normalement un tableau en tant qu'objet contenant le tableau. Prenons l'exemple suivant :
<code>{ "items": { "number": 3, "item": [ { ... } // Not relevant ] } } </code>
L'API fait cela à des dizaines d'endroits, à chaque fois avec un nom différent. Cela est garanti avec seulement deux clés : l’une d’elles est number
et l’autre est un tableau.
Cela rend la structure résultante plutôt désagréable à utiliser, car vous devez constamment naviguer à travers des niveaux de champs inutiles.
Je veux essentiellement que mon interface Go fasse semblant d'avoir ce format :
<code>{ "items": [ { ... } // Not relevant ] } </code>
Une option consiste à écrire une fonction UnmarshalJSON
personnalisée pour chaque occurrence, mais cela semble fastidieux, d'autant plus qu'elle apparaît dans presque toutes les structures. La solution que j'ai proposée est un type générique qui peut le gérer seul.
Ma tentative actuelle est la suivante :
<code>// NestedArray tries to pull an unnecessarily nested array upwards type NestedArray[T any] []T func (n *NestedArray[T]) UnmarshalJSON(bytes []byte) error { // First unmarshal into a map target := make(map[string]interface{}) err := json.Unmarshal(bytes, &target) if err != nil { return err } // Then find the nested array (key is unknown, so go off of the type instead) var sliceVal interface{} for k, v := range target { if k == "number" { continue } rt := reflect.TypeOf(v) if rt.Kind() == reflect.Slice { sliceVal = v break } } // Missing or empty, doesn't matter - set the result to nil if sliceVal == nil { *n = nil return nil } // Turn back into JSON and parse into correct target sliceJSON, err := json.Marshal(sliceVal) if err != nil { return err } err = json.Unmarshal(sliceJSON, n) // Error occurs here if err != nil { return err } return nil } </code>
Comment l'utiliser est le suivant :
<code>type Item struct { // Not relevant } type Root struct { // Use generic type to parse a JSON object into its nested array Items NestedArray[Item] `json:"items,omitempty"` } </code>
entraîne l'erreur suivante :
json: cannot unmarshal array into Go struct field Root.items of type map[string]interface{}
UnmarshalJSON
代码的最大部分似乎是正确的,因为我的调试器向我显示 sliceVal
正是我所期望的。解组回 NestedArray[T]
La plus grande partie du code semble être correcte, car mon débogueur me montre que sliceVal
est exactement ce à quoi je m'attends. Erreur lors du rétablissement du type NestedArray[T]
.
Existe-t-il un moyen de résoudre ce problème ? Existe-t-il un meilleur moyen que ce que je fais actuellement ? Cela me semble le plus propre, mais je suis ouvert aux suggestions.
La méthode NestedArray[T].UnmarshalJSON s'appelle de manière récursive. L'appel interne génère une erreur car il nécessite bytes
中的 JSON 对象,但它收到了一个 JSON 数组。通过解组到 []T
而不是 NestedArray[T]
pour être corrigé.
Sans rapport avec l'erreur, la méthode NestedArray[T].UnmarshalJSON a effectué un encodage et un décodage inutiles. Utilisez json.RawMessage pour le réparer.
Voici le code avec les deux correctifs :
func (n *NestedArray[T]) UnmarshalJSON(bytes []byte) error { // First unmarshal into a map var target map[string]json.RawMessage err := json.Unmarshal(bytes, &target) if err != nil { return err } // Then find the nested array (key is unknown, so go off of the type instead) var array json.RawMessage for k, v := range target { if k == "number" { continue } if len(v) > 0 && v[0] == '[' { array = v break } } // Missing or empty, doesn't matter - set the result to nil if array == nil { *n = nil return nil } // Avoid recursive call to this method by unmarshalling to a []T. var v []T err = json.Unmarshal(array, &v) *n = v return err }
Exécutez du code sur le Playground ! .
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!