Criando um JSON Unmarshal personalizado em Go
Tags: go, unmarshal, struct, json
Published: 2023-09-13 11:58:54

---
title: Criando um JSON Unmarshal personalizado em Go
published: yes
description: Na busca por entender como substituir corretamente o UnmarshalJSON, ou seja, personalizar o json.Unmarshal em sua estrutura Go...
tags: go, unmarshal, struct, json
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-09-12 17:20 +0000
---

Neste breve tutorial, vamos explorar como criar um método UnmarshalJSON personalizado em Go, adaptando-o para uma situação em que temos um campo específico na estrutura que pode ser representado como número, booleano ou string. Isto nos permitirá lidar com dados JSON de forma flexível e personalizada.

---

Vamos prosseguir com um exemplo prático usando a estrutura "King" com os campos "Active" e "Name". Vamos começar a criar o método `UnmarshalJSON` personalizado para lidar com o campo "Active" que pode ser representado como número, booleano ou string.
```go
type King struct {
	Name   string `json:"name"`
	Active bool   `json:"active,string"`
}
//Após criar a estrutura vamos implementar a função para o UnmarshalJSON personalizado.

func (k *King) UnmarshalJSON(data []byte) (err error) {
	// Criamos um mapa dinâmico para armazenar os campos JSON.
	var fields map[string]interface{}

	// Realizamos a decodificação JSON padrão no mapa "fields".
	err = json.Unmarshal(data, &fields)
	if err != nil {
		return err
	}

	// Copiamos o valor do campo "name" do mapa para a estrutura "King".
	k.Name = fields["name"].(string)

	// Convertendo o valor do campo "active" para booleano.
	// Usamos `fmt.Sprintf` para garantir que o valor seja uma string antes da conversão.
	activeStr := fmt.Sprintf("%v", fields["active"])

	// Aqui estamos convertendo para boleano e ignoramos o erro pois se vier algo diferente do esperado sempre será falso
	k.Active, _ = strconv.ParseBool(activeStr)

	return nil
}
```

Agora que entendemos como criar um Unmarshal JSON personalizado, vamos aplicar isso na função principal, chamada 'main', do pacote principal. A seguir, apresentamos o código com os resultados esperados.
```go
func main() {
	var k King
	if err := json.Unmarshal([]byte(`{"active": 1, "name":"renan"}`), &k); err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", k)
	// Output: {Name:renan Active:true}
}
```

Para concluir, demonstraremos um teste unitário usando nosso método personalizado, abrangendo todos os casos para uma compreensão abrangente.
```go

func TestKing_UnmarshalJSON(t *testing.T) {
	tests := []struct {
		name    string
		king    King
		data    []byte
		wantErr bool
	}{
		{
			name:    "Employing the number 1 to denote a true value.",
			king:    King{Name: "renan", Active: true},
			data:    []byte(`{"active": 1, "name":"renan"}`),
			wantErr: false,
		},
		{
			name:    "Employing the number 0 to denote a false value.",
			king:    King{Name: "renan", Active: false},
			data:    []byte(`{"active": 0, "name":"renan"}`),
			wantErr: false,
		},
		{
			name:    "Utilizing the string '1' to represent the boolean value 'true'.",
			king:    King{Name: "renan", Active: true},
			data:    []byte(`{"active": "1", "name":"renan"}`),
			wantErr: false,
		},
		{
			name:    "Utilizing the string '0' to represent the boolean value 'false'.",
			king:    King{Name: "renan", Active: false},
			data:    []byte(`{"active": "0", "name":"renan"}`),
			wantErr: false,
		},
		{
			name:    "Utilizing the string 'true' to represent the boolean value 'true'",
			king:    King{Name: "renan", Active: true},
			data:    []byte(`{"active": "true", "name":"renan"}`),
			wantErr: false,
		},
		{
			name:    "Utilizing the bool true to represent the boolean value 'true'",
			king:    King{Name: "renan", Active: true},
			data:    []byte(`{"active": true, "name":"renan"}`),
			wantErr: false,
		},
		{
			name:    "Utilizing the bool false to represent the boolean value 'false'",
			king:    King{Name: "renan", Active: false},
			data:    []byte(`{"active": false, "name":"renan"}`),
			wantErr: false,
		},
		{
			name:    "Utilizing the string 'false' to represent the boolean value 'false'",
			king:    King{Name: "renan", Active: false},
			data:    []byte(`{"active": "false", "name":"renan"}`),
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var k King
			err := json.Unmarshal(tt.data, &k)
			if (err != nil) != tt.wantErr {
				t.Errorf("King.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
			}
			if k.Active != tt.king.Active || k.Name != tt.king.Name {
				t.Errorf("unmarshal failed: k[%+v]; tt.kink[%+v];", k, tt.king)
			}
		})
	}
}
```

Agradeço por acompanhar este tutorial. Se você tiver alguma dúvida, comentário ou sugestão, por favor, não hesite em entrar em contato comigo. Estou à disposição para ajudar e melhorar.