检查结构字段是否为空

I would like to iterate over the fields of a struct after unmarshalling a JSON object into it and check for the fields whose value was not set (i.e. are empty).

I can get the value of each field and compare that to the reflect.Zero value for the corresponding type

json.Unmarshal([]byte(str), &res)
s := reflect.ValueOf(&res).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
    f := s.Field(i)
    v := reflect.ValueOf(f.Interface())
    if (reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())) {
    ....

But the problem, of course, is that this will not work well for bool or int values. If a bool field is set to false in the JSON or an int field is set to 0, they will be equal to the zero value of their type. The aforementioned check will consider the fields to be uninitialized, even though they actually have a value set.

I know one way around this is to use pointers, but I just don't see how that would be possible in this case as I'm working with reflect.Value types, not the actual struct.

As you've mentioned, you could use pointers.

The json package can handle unmarshalling values into pointers for you. You've not included the json payload you are trying to unmarshal, or the struct you are unmarshalling into, so I've made up an example.

// json
{
    "foo": true,
    "number_of_foos": 14
}

// go struct
type Foo struct {
    Present bool `json:"foo"`
    Num     int  `json:"number_of_foos"`
}

Here if the keys foo or number_of_foos is missing, then as you've correctly observed, the zero value (false/ 0) will be used. In general the best advice is to make use of the zero value. Create structures so that zero values of false are useful, rather than a pain. This is not always possible, so changing the types of the fields in the Foo struct to be pointers will allow you to check the 3 cases you are after.

  1. Present
  2. Present and zero
  3. Missing

here is the same struct with pointers:

// go struct
type Foo struct {
    Present *bool `json:"foo"`
    Num     *int  `json:"number_of_foos"`
}

Now you can check for presence of the value with fooStruct.Present != nil and if that condition holds, you can assume that the value in the field is the one you wanted.

There is no need to use the reflect package.

Another way of doing the same is by implementing json.Unmarshaler.

type MaybeInt struct {
    Present bool
    Value   int
}

func (i *MaybeInt) UnmarshalJSON(bs []byte) error {
    if e := json.Unmarshal(bs, &i.Value); e != nil {
        return e
    }
    i.Present = true
    return nil
}

You can then use MaybeInt in your top-level structure:

type Top struct {
    N MaybeInt `json:"n"`
    M MaybeInt `json:"m"`
}

func main() {
    t := Top{}
    if e := json.Unmarshal([]byte(` { "n": 4930 } `), &t); e != nil {
        panic(e)
    }
    fmt.Println(t.N, t.M)
}

See it working on the playground

Try using the golang validator package. The validator package offers a required attribute that might do the required job for your need. The official documentation for required attribute states:

This validates that the value is not the data types default zero value. For numbers ensures value is not zero. For strings ensures value is not "". For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil.

The example illustrating the same can be seen at: https://github.com/go-playground/validator/blob/v9/_examples/struct-level/main.go.

Hope this solves your requirement.