Heim >Backend-Entwicklung >Golang >Generische Typdefinition zum Unmarshalling von Strukturen in Slices
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.
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!