Skip to content

Commit 1e9514f

Browse files
Add MarshalWithOpts method to have fine-grained control over data masking
1 parent 40e2589 commit 1e9514f

File tree

1 file changed

+40
-1
lines changed

1 file changed

+40
-1
lines changed

src/encoding/json/encode.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,31 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
181181
return buf.Bytes(), nil
182182
}
183183

184+
// MarshalWithOpts is like Marshal but applies custom Marshalling, including data masking and indentation to format the output.
185+
// The Opts interface specifies methods to control:
186+
// - Output prefix and indentation options
187+
// - Omitting empty values (e.g. empty strings)
188+
// - Custom data masking to have fine-grained control over which specific fields are omitted during serialization.
189+
// Each JSON element in the output will begin on a new line beginning with prefix
190+
// followed by one or more copies of indent according to the indentation nesting.
191+
func MarshalWithOpts(v interface{}, opts Opts) ([]byte, error) {
192+
e := &encodeState{}
193+
err := e.marshal(v, encOpts{escapeHTML: true, optx: opts})
194+
if err != nil {
195+
return nil, err
196+
}
197+
if opts.Indent() != "" {
198+
var buf bytes.Buffer
199+
err = Indent(&buf, e.Bytes(), opts.Prefix(), opts.Indent())
200+
if err != nil {
201+
return nil, err
202+
}
203+
return buf.Bytes(), nil
204+
} else {
205+
return e.Bytes(), nil
206+
}
207+
}
208+
184209
// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
185210
// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
186211
// so that the JSON will be safe to embed inside HTML <script> tags.
@@ -260,6 +285,13 @@ type MarshalerError struct {
260285
Err error
261286
}
262287

288+
type Opts interface {
289+
OmitEmpty() bool // Exclude fields that have empty values.
290+
Omit(s reflect.Value, f string) bool // Return true if field 'f' of struct value 's' should be omitted.
291+
Prefix() string // Each JSON element in the output will begin on a new line beginning with this prefix
292+
Indent() string // The indentation string.
293+
}
294+
263295
func (e *MarshalerError) Error() string {
264296
return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Err.Error()
265297
}
@@ -330,6 +362,8 @@ type encOpts struct {
330362
quoted bool
331363
// escapeHTML causes '<', '>', and '&' to be escaped in JSON strings.
332364
escapeHTML bool
365+
// Omit empty
366+
optx Opts
333367
}
334368

335369
type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts)
@@ -620,12 +654,17 @@ type structEncoder struct {
620654
fieldEncs []encoderFunc
621655
}
622656

657+
func (se *structEncoder) omitField(v reflect.Value, fv reflect.Value, f field, opts encOpts) bool {
658+
return ((f.omitEmpty || (opts.optx != nil && opts.optx.OmitEmpty())) && isEmptyValue(fv)) ||
659+
(opts.optx != nil && opts.optx.Omit(v, f.name))
660+
}
661+
623662
func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
624663
e.WriteByte('{')
625664
first := true
626665
for i, f := range se.fields {
627666
fv := fieldByIndex(v, f.index)
628-
if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
667+
if !fv.IsValid() || se.omitField(v, fv, f, opts) {
629668
continue
630669
}
631670
if first {

0 commit comments

Comments
 (0)