Add omitzero tag support (#998)

This commit is contained in:
Nathan Baulch
2025-08-25 16:06:48 +10:00
committed by GitHub
parent 644602b845
commit c57d0d559f
2 changed files with 86 additions and 1 deletions
+20
View File
@@ -161,6 +161,8 @@ func (enc *Encoder) SetMarshalJsonNumbers(indent bool) *Encoder {
// //
// The "omitempty" option prevents empty values or groups from being emitted. // The "omitempty" option prevents empty values or groups from being emitted.
// //
// The "omitzero" option prevents zero values or groups from being emitted.
//
// The "commented" option prefixes the value and all its children with a comment // The "commented" option prefixes the value and all its children with a comment
// symbol. // symbol.
// //
@@ -196,6 +198,7 @@ func (enc *Encoder) Encode(v interface{}) error {
type valueOptions struct { type valueOptions struct {
multiline bool multiline bool
omitempty bool omitempty bool
omitzero bool
commented bool commented bool
comment string comment string
} }
@@ -384,6 +387,10 @@ func shouldOmitEmpty(options valueOptions, v reflect.Value) bool {
return options.omitempty && isEmptyValue(v) return options.omitempty && isEmptyValue(v)
} }
func shouldOmitZero(options valueOptions, v reflect.Value) bool {
return options.omitzero && v.IsZero()
}
func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) { func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) {
var err error var err error
@@ -774,6 +781,7 @@ func walkStruct(ctx encoderCtx, t *table, v reflect.Value) {
options := valueOptions{ options := valueOptions{
multiline: opts.multiline, multiline: opts.multiline,
omitempty: opts.omitempty, omitempty: opts.omitempty,
omitzero: opts.omitzero,
commented: opts.commented, commented: opts.commented,
comment: fieldType.Tag.Get("comment"), comment: fieldType.Tag.Get("comment"),
} }
@@ -834,6 +842,7 @@ type tagOptions struct {
multiline bool multiline bool
inline bool inline bool
omitempty bool omitempty bool
omitzero bool
commented bool commented bool
} }
@@ -862,6 +871,8 @@ func parseTag(tag string) (string, tagOptions) {
opts.inline = true opts.inline = true
case "omitempty": case "omitempty":
opts.omitempty = true opts.omitempty = true
case "omitzero":
opts.omitzero = true
case "commented": case "commented":
opts.commented = true opts.commented = true
} }
@@ -896,6 +907,9 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro
if shouldOmitEmpty(kv.Options, kv.Value) { if shouldOmitEmpty(kv.Options, kv.Value) {
continue continue
} }
if shouldOmitZero(kv.Options, kv.Value) {
continue
}
hasNonEmptyKV = true hasNonEmptyKV = true
ctx.setKey(kv.Key) ctx.setKey(kv.Key)
@@ -915,6 +929,9 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro
if shouldOmitEmpty(table.Options, table.Value) { if shouldOmitEmpty(table.Options, table.Value) {
continue continue
} }
if shouldOmitZero(table.Options, table.Value) {
continue
}
if first { if first {
first = false first = false
if hasNonEmptyKV { if hasNonEmptyKV {
@@ -949,6 +966,9 @@ func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte
if shouldOmitEmpty(kv.Options, kv.Value) { if shouldOmitEmpty(kv.Options, kv.Value) {
continue continue
} }
if shouldOmitZero(kv.Options, kv.Value) {
continue
}
if first { if first {
first = false first = false
+66 -1
View File
@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
"net/netip"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@@ -1117,6 +1118,9 @@ func TestEncoderOmitempty(t *testing.T) {
Ptr *string `toml:",omitempty,multiline"` Ptr *string `toml:",omitempty,multiline"`
Iface interface{} `toml:",omitempty,multiline"` Iface interface{} `toml:",omitempty,multiline"`
Struct struct{} `toml:",omitempty,multiline"` Struct struct{} `toml:",omitempty,multiline"`
Inline struct {
String string `toml:",omitempty,multiline"`
} `toml:",inline"`
} }
d := doc{} d := doc{}
@@ -1124,7 +1128,68 @@ func TestEncoderOmitempty(t *testing.T) {
b, err := toml.Marshal(d) b, err := toml.Marshal(d)
assert.NoError(t, err) assert.NoError(t, err)
expected := `` expected := `Inline = {}
`
assert.Equal(t, expected, string(b))
}
func TestEncoderOmitzero(t *testing.T) {
type doc struct {
String string `toml:",omitzero,multiline"`
Bool bool `toml:",omitzero,multiline"`
Int int `toml:",omitzero,multiline"`
Int8 int8 `toml:",omitzero,multiline"`
Int16 int16 `toml:",omitzero,multiline"`
Int32 int32 `toml:",omitzero,multiline"`
Int64 int64 `toml:",omitzero,multiline"`
Uint uint `toml:",omitzero,multiline"`
Uint8 uint8 `toml:",omitzero,multiline"`
Uint16 uint16 `toml:",omitzero,multiline"`
Uint32 uint32 `toml:",omitzero,multiline"`
Uint64 uint64 `toml:",omitzero,multiline"`
Float32 float32 `toml:",omitzero,multiline"`
Float64 float64 `toml:",omitzero,multiline"`
MapNil map[string]string `toml:",omitzero,multiline"`
Slice []string `toml:",omitzero,multiline"`
Ptr *string `toml:",omitzero,multiline"`
Iface interface{} `toml:",omitzero,multiline"`
Struct struct{} `toml:",omitzero,multiline"`
Time time.Time `toml:",omitzero,multiline"`
IP netip.Addr `toml:",omitzero,multiline"`
Inline struct {
String string `toml:",omitzero,multiline"`
} `toml:",inline"`
}
d := doc{}
b, err := toml.Marshal(d)
assert.NoError(t, err)
expected := `Inline = {}
`
assert.Equal(t, expected, string(b))
}
func TestEncoderOmitzeroOpaqueStruct(t *testing.T) {
type doc struct {
Time time.Time `toml:",omitzero"`
IP netip.Addr `toml:",omitzero"`
}
d := doc{
Time: time.Date(2001, 2, 3, 4, 5, 6, 7, time.UTC),
IP: netip.MustParseAddr("192.168.178.35"),
}
b, err := toml.Marshal(d)
assert.NoError(t, err)
expected := `Time = 2001-02-03T04:05:06.000000007Z
IP = '192.168.178.35'
`
assert.Equal(t, expected, string(b)) assert.Equal(t, expected, string(b))
} }