Maison  >  Article  >  développement back-end  >  Définition de type générique pour désorganiser les structures en tranches

Définition de type générique pour désorganiser les structures en tranches

王林
王林avant
2024-02-06 08:54:041082parcourir

Définition de type générique pour désorganiser les structures en tranches

Contenu de la question

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.


Bonne réponse


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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer