GO | Recentemente, recebi um retorno de uma API que continha um JSON com uma estrutura complexa, grande e aninhada. Eu precisava acessar um valor específico dentro dessa estrutura, mas o caminho para esse valor poderia variar.
A ideia então foi criar uma função recursiva que recebesse uma lista com o caminho (path) para o valor desejado e a estrutura de dados (um mapa aninhado) e retornasse o valor correspondente, se encontrado.
Acredito que existem maneiras mais espertas de fazer isso, mas essa abordagem recursiva funcionou bem para o meu caso.
package main
import (
"fmt"
)
func GetFromMap(parts []string, current any) (any, bool) {
fmt.Printf("parts: %v, current: %v\n", parts, current)
if len(parts) == 0 {
return current, true
}
m, ok := current.(map[string]any)
if !ok { // not a map, cannot descend
return nil, false
}
key := parts[0]
next, exists := m[key]
if !exists {
return nil, false
}
return GetFromMap(parts[1:], next)
}
func MustGetFromMap(path []string, data any) any {
val, ok := GetFromMap(path, data)
if !ok {
panic(fmt.Sprintf("path %v not found in map", path))
}
return val
}
E aqui está um exemplo de como você pode testar essa função:
package main
import "testing"
func TestGetFromMap(t *testing.T) {
data := map[string]any{
"not_a_map": "just a string",
"a": map[string]any{
"b": map[string]any{
"c": 42,
},
"d": "hello",
},
"x": 3.14,
}
tests := []struct {
path []string
expectedVal any
expectedFound bool
}{
{
path: []string{"a", "b", "c"},
expectedVal: 42,
expectedFound: true,
},
{
path: []string{"a", "d"},
expectedVal: "hello",
expectedFound: true,
},
{
path: []string{"x"},
expectedVal: 3.14,
expectedFound: true,
},
{
path: []string{"a", "not_found"},
expectedVal: nil,
expectedFound: false,
},
}
for _, tt := range tests {
val, found := GetFromMap(tt.path, data)
if found != tt.expectedFound {
t.Errorf("GetFromMap(%v) found = %v; want %v", tt.path, found, tt.expectedFound)
}
if val != tt.expectedVal {
t.Errorf("GetFromMap(%v) = %v; want %v", tt.path, val, tt.expectedVal)
}
}
}
E, por favor, quando for criar uma API, lembre-se do seu colega programador que vai consumir a API. Estruturas simples e planas que não mudam de formato com frequência são melhores. Aliás, simples sempre é melhor.