Search code examples
jsongomarshalling

How to json marshal without omitempty on struct fields


I have a generated type A,

I want to json.Marshal the type and to ignore any empty fields while testing.

The generated type comes without json:",omitempty" for any struct fields, nor would I want to for purposes of the application itself.

What would be the best way to marshal my type without providing empty fields?

Cheers!


Solution

  • The simples way is crate new type for testing with the same field as A and use one in tests:

    type A struct {
        Val  string `json:"val"`
        Val2 int    `json:"val2"`
    }
    
    type testA struct {
        Val  string `json:"val,omitempty"`
        Val2 int    `json:"val2,omitempty"`
    }
    
    func Test(t *testing.T) {
        a := A{
            Val:  "some",
            Val2: 0,
        }
    
        t.Run("cast", func(t *testing.T) {
            ab, _ := json.Marshal(a)
            t.Log(string(ab))
            ab, _ = json.Marshal(testA(a))
            t.Log(string(ab))
        })
    }
    
    === RUN   Test
    === RUN   Test/cust
        some_test.go:26: {"val":"some","val2":0}
        some_test.go:28: {"val":"some"}
    --- PASS: Test (0.00s)
        --- PASS: Test/cust (0.00s)
    

    PLAYGROUND

    Other way is creating new type base on A, implements Marshaler for new type and create struct with the same fields and tags (with omitempty) dynamically use reflect package:

    type testAV2 A
    
    func (u testAV2) MarshalJSON() ([]byte, error) {
        value := reflect.ValueOf(u)
        t := value.Type()
        sf := make([]reflect.StructField, 0)
        // modify the 'fori' snippet for more complicated cases 
        for i := 0; i < t.NumField(); i++ {
            sf = append(sf, t.Field(i))
            tag := t.Field(i).Tag
            if !strings.Contains(string(tag), ",omitempty") {
                r := regexp.MustCompile(`json:"\s*(.*?)\s*"`)
                matches := r.FindAllStringSubmatch(string(tag), -1)
                for _, v := range matches {
                    tagKey := v[1]
                    sf[i].Tag = reflect.StructTag(fmt.Sprintf(`json:"%s,omitempty"`, tagKey))
                }
            }
        }
        newType := reflect.StructOf(sf)
        newValue := value.Convert(newType)
        return json.Marshal(newValue.Interface())
    }
    

    PLAYGROUND