#1413·validator

Is there a way not to include name of Embedded structure in Namespace for error?

Author: arxeissCreated Apr 10, 2025Updated Apr 23, 2026

We are using Validator to validate incoming JSON requests. And because all requests contains common attributes we are using embedding (Human in the example below).

Since that embedding has no tags (in Adult), it works exactly as we want. When json.Unmarshal (or json.Marshal) is called it works as we want. In example below, first_name and last_name are in JSON as top-level attributes.

But the issue happen during validation. Because the error contains those errors:

  • "Adult.id_card_number": "Failed on tag: required"
  • "Adult.Human.first_name": "Failed on tag: min",
  • "Adult.address.city": "Failed on tag: required",

The fact, that "Adult" is there we don't care much. It is easy just to remove everything before first ..

After we do trimming, we will have id_card_number and address.city keys which are correct. They respect same structure as input JSON. But Human.first_name is the issue. Because in input JSON there is no "Human" key at all.

My question is, if there is a way not to include name of embedded structure into namespace?

go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"

	"github.com/go-playground/validator/v10"
)

type Human struct {
	FirstName string `json:"first_name" validate:"required,min=2"`
	LastName  string `json:"last_name" validate:"required,min=2"`
}

type Address struct {
	Street string `json:"street" validate:"required,min=2"`
	City   string `json:"city" validate:"required,min=2"`
}

// User contains user information
type Adult struct {
	Human
	Address      Address `json:"address"`
	IdCardNumber string  `json:"id_card_number" validate:"required,min=10"`
}

// use a single instance of Validate, it caches struct info
var validate *validator.Validate

func main() {
	validate = validator.New(func(v *validator.Validate) {
		v.RegisterTagNameFunc(func(fld reflect.StructField) string {
			name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
			// skip if tag key says it should be ignored
			if name == "-" {
				return ""
			}
			return name
		})
	})

	data := Adult{}
	err := json.Unmarshal([]byte(`{
		"first_name":"I",
		"last_name":"O",
		"address":{
			"street":"X"
		}
	}`), &data)

	if err != nil {
		panic(err)
	}

	err = validate.Struct(data)
	jsonFieldErrResponse := map[string]string{}
	jsonNsErrResponse := map[string]string{}
	if err != nil {
		for _, err := range err.(validator.ValidationErrors) {
			jsonFieldErrResponse[err.Field()] = "Failed on tag: " + err.ActualTag()
			jsonNsErrResponse[err.Namespace()] = "Failed on tag: " + err.ActualTag()
		}
	}

	d, _ := json.Marshal(jsonFieldErrResponse)
	var df bytes.Buffer
	json.Indent(&df, d, "", "  ")
	fmt.Println("Field err:", df.String())

	d, _ = json.Marshal(jsonNsErrResponse)
	df.Reset()
	json.Indent(&df, d, "", "  ")
	fmt.Printf("Namespace err: %s", df.String())
}

Output from above the code is

json
Field err: {
  "city": "Failed on tag: required",
  "first_name": "Failed on tag: min",
  "id_card_number": "Failed on tag: required",
  "last_name": "Failed on tag: min",
  "street": "Failed on tag: min"
}
Namespace err: {
  "Adult.Human.first_name": "Failed on tag: min",
  "Adult.Human.last_name": "Failed on tag: min",
  "Adult.address.city": "Failed on tag: required",
  "Adult.address.street": "Failed on tag: min",
  "Adult.id_card_number": "Failed on tag: required"
}