Heim  >  Artikel  >  Backend-Entwicklung  >  Generische Typdefinition zum Unmarshalling von Strukturen in Slices

Generische Typdefinition zum Unmarshalling von Strukturen in Slices

王林
王林nach vorne
2024-02-06 08:54:041082Durchsuche

Generische Typdefinition zum Unmarshalling von Strukturen in Slices

Frageninhalt

Ich habe eine API, die normalerweise ein Array als Objekt zurückgibt, das das Array enthält. Nehmen Sie als Beispiel das folgende Beispiel:

<code>{
  "items": {
    "number": 3,
    "item": [
      { ... } // Not relevant
    ]
  }
}
</code>

Die API führt dies an Dutzenden von Stellen durch, jedes Mal unter einem anderen Namen. Dies geschieht garantiert mit nur zwei Schlüsseln: Einer davon ist number und der andere ist ein Array.

Dadurch ist die Nutzung der resultierenden Struktur eher unangenehm, da man ständig durch unnötige Feldebenen navigieren muss.

Ich möchte im Wesentlichen, dass meine Go-Oberfläche so tut, als hätte sie dieses Format:

<code>{
  "items": [
    { ... } // Not relevant
  ]
}
</code>

Eine Möglichkeit besteht darin, für jedes Vorkommen eine benutzerdefinierte UnmarshalJSON Funktion zu schreiben. Dies erscheint jedoch umständlich, insbesondere wenn man bedenkt, dass sie in fast jeder Struktur vorkommt. Die Lösung, die ich gefunden habe, ist ein generischer Typ, der alleine damit umgehen kann.

Mein aktueller Versuch ist wie folgt:

<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>

Die Anwendung ist wie folgt:

<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>

führt zu folgendem Fehler:

json: cannot unmarshal array into Go struct field Root.items of type map[string]interface{}

UnmarshalJSON 代码的最大部分似乎是正确的,因为我的调试器向我显示 sliceVal 正是我所期望的。解组回 NestedArray[T] Der größte Teil des Codes scheint korrekt zu sein, da mein Debugger mir zeigt, dass sliceVal genau das ist, was ich erwarte. Fehler beim Unmarshalling zurück zum Typ NestedArray[T].

Gibt es eine Möglichkeit, dieses Problem zu lösen? Gibt es einen besseren Weg als das, was ich jetzt mache? Das erscheint mir am saubersten, aber ich bin offen für Vorschläge.


Richtige Antwort


Die Methode NestedArray[T].UnmarshalJSON ruft sich selbst rekursiv auf. Der interne Aufruf löst einen Fehler aus, da zur Behebung bytes 中的 JSON 对象,但它收到了一个 JSON 数组。通过解组到 []T 而不是 NestedArray[T] erforderlich ist.

Unabhängig vom Fehler führte die Methode NestedArray[T].UnmarshalJSON einige unnötige Kodierungen und Dekodierungen durch. Verwenden Sie json.RawMessage, um das Problem zu beheben.

Hier ist der Code mit beiden Fixes:

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
}

Führen Sie Code auf dem Spielplatz aus! .

Das obige ist der detaillierte Inhalt vonGenerische Typdefinition zum Unmarshalling von Strukturen in Slices. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen