From b5cd1162191a8163b7d2b0fbc28c7a91b05b9264 Mon Sep 17 00:00:00 2001 From: Timo Vetter Date: Thu, 5 Oct 2023 10:45:09 +0200 Subject: [PATCH] DYN-166 add jsonfilter to json library --- gojson/encode.go | 52 ++++++++++++++++++++++++++++++++----------- gojson/encode_test.go | 8 ++++++- gojson/gionic.go | 3 ++- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/gojson/encode.go b/gojson/encode.go index 9f521e1..765e30e 100644 --- a/gojson/encode.go +++ b/gojson/encode.go @@ -174,9 +174,9 @@ type IndentOpt struct { // MarshalSafeCollections is like Marshal except it will marshal nil maps and // slices as '{}' and '[]' respectfully instead of 'null' -func MarshalSafeCollections(v interface{}, nilSafeSlices bool, nilSafeMaps bool, indent *IndentOpt) ([]byte, error) { +func MarshalSafeCollections(v interface{}, nilSafeSlices bool, nilSafeMaps bool, indent *IndentOpt, filter *string) ([]byte, error) { e := &encodeState{} - err := e.marshal(v, encOpts{escapeHTML: true, nilSafeSlices: nilSafeSlices, nilSafeMaps: nilSafeMaps}) + err := e.marshal(v, encOpts{escapeHTML: true, nilSafeSlices: nilSafeSlices, nilSafeMaps: nilSafeMaps, filter: filter}) if err != nil { return nil, err } @@ -393,6 +393,9 @@ type encOpts struct { nilSafeSlices bool // nilSafeMaps marshals a nil maps '{}' instead of 'null' nilSafeMaps bool + // filter matches jsonfilter tag of struct + // marshals if no jsonfilter is set or otherwise if jsonfilter has the filter value + filter *string } type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts) @@ -777,6 +780,8 @@ FieldLoop: if f.omitEmpty && isEmptyValue(fv) { continue + } else if opts.filter != nil && len(f.jsonfilter) > 0 && !f.jsonfilter.Contains(*opts.filter) { + continue } e.WriteByte(next) next = ',' @@ -1220,15 +1225,28 @@ type field struct { nameNonEsc string // `"` + name + `":` nameEscHTML string // `"` + HTMLEscape(name) + `":` - tag bool - index []int - typ reflect.Type - omitEmpty bool - quoted bool + tag bool + index []int + typ reflect.Type + omitEmpty bool + jsonfilter jsonfilter + quoted bool encoder encoderFunc } +// jsonfilter stores the value of the jsonfilter struct tag +type jsonfilter []string + +func (j jsonfilter) Contains(t string) bool { + for _, tag := range j { + if t == tag { + return true + } + } + return false +} + // byIndex sorts field by index sequence. type byIndex []field @@ -1304,6 +1322,13 @@ func typeFields(t reflect.Type) structFields { if !isValidTag(name) { name = "" } + + var jsonfilter []string + jsonfilterTag := sf.Tag.Get("jsonfilter") + if isValidTag(jsonfilterTag) { + jsonfilter = strings.Split(jsonfilterTag, ",") + } + index := make([]int, len(f.index)+1) copy(index, f.index) index[len(f.index)] = i @@ -1334,12 +1359,13 @@ func typeFields(t reflect.Type) structFields { name = sf.Name } field := field{ - name: name, - tag: tagged, - index: index, - typ: ft, - omitEmpty: opts.Contains("omitempty"), - quoted: quoted, + name: name, + tag: tagged, + index: index, + typ: ft, + omitEmpty: opts.Contains("omitempty"), + jsonfilter: jsonfilter, + quoted: quoted, } field.nameBytes = []byte(field.name) field.equalFold = foldFunc(field.nameBytes) diff --git a/gojson/encode_test.go b/gojson/encode_test.go index e3f35c6..104a78f 100644 --- a/gojson/encode_test.go +++ b/gojson/encode_test.go @@ -1253,6 +1253,10 @@ func TestMarshalSafeCollections(t *testing.T) { nilMapStruct struct { NilMap map[string]interface{} `json:"nil_map"` } + testWithFilter struct { + Test1 string `json:"test1" jsonfilter:"FILTERONE"` + Test2 string `json:"test2" jsonfilter:"FILTERTWO"` + } ) tests := []struct { @@ -1271,10 +1275,12 @@ func TestMarshalSafeCollections(t *testing.T) { {map[string]interface{}{"1": 1, "2": 2, "3": 3}, "{\"1\":1,\"2\":2,\"3\":3}"}, {pNilMap, "null"}, {nilMapStruct{}, "{\"nil_map\":{}}"}, + {testWithFilter{}, "{\"test1\":\"\"}"}, } + filter := "FILTERONE" for i, tt := range tests { - b, err := MarshalSafeCollections(tt.in, true, true, nil) + b, err := MarshalSafeCollections(tt.in, true, true, nil, &filter) if err != nil { t.Errorf("test %d, unexpected failure: %v", i, err) } diff --git a/gojson/gionic.go b/gojson/gionic.go index 2bd18f9..005218c 100644 --- a/gojson/gionic.go +++ b/gojson/gionic.go @@ -17,6 +17,7 @@ type GoJsonRender struct { NilSafeSlices bool NilSafeMaps bool Indent *IndentOpt + Filter *string } func (r GoJsonRender) Render(w http.ResponseWriter) error { @@ -25,7 +26,7 @@ func (r GoJsonRender) Render(w http.ResponseWriter) error { header["Content-Type"] = []string{"application/json; charset=utf-8"} } - jsonBytes, err := MarshalSafeCollections(r.Data, r.NilSafeSlices, r.NilSafeMaps, r.Indent) + jsonBytes, err := MarshalSafeCollections(r.Data, r.NilSafeSlices, r.NilSafeMaps, r.Indent, r.Filter) if err != nil { panic(err) }