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.
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.
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.
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.
Top comments (2)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.