Repository: goccy/go-yaml
Pull request #691 (2025-04-11 09:53:14)

feat: add global `OmitEmpty` encoding option
Thanks for an amazing project! Go ecosystem struggle (e.g. https://github.com/GoogleCloudPlatform/prometheus-engine/issues/1629) with 3P types that lack a good marshalling practices. Many projects have config structs only designed for parsing. See https://github.com/goccy/go-yaml/issues/306 for the detailed motivation.

Fixes: https://github.com/goccy/go-yaml/issues/306

The feature mechanism is simple -- we ignore struct tag setting for omitempty. We assume it's always 'omitempty' if yaml.OmitEmpty() setting is set.

I confirm:

- [X] Describe the purpose for which you created this PR.  
- [X] Create test code that corresponds to the modification

codecov-commenter: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/goccy/go-yaml/pull/691?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 77.89%. Comparing base [(`ba0598a`)](https://app.codecov.io/gh/goccy/go-yaml/commit/ba0598a7f0c9b3fd0b70393b430e689d58b27281?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima) to head [(`968acd5`)](https://app.codecov.io/gh/goccy/go-yaml/commit/968acd5ebdf60c6590ee73c960dac5b7eca9daf1?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima).

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #691      +/-   ##
==========================================
+ Coverage   77.87%   77.89%   +0.01%     
==========================================
  Files          22       22              
  Lines        7960     7965       +5     
==========================================
+ Hits         6199     6204       +5     
  Misses       1348     1348              
  Partials      413      413              
```

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
goccy: Thank you for your great PR !!! LGTM 👍 

Patch:
diff --git a/encode.go b/encode.go
index e0c795e..813d7fc 100644
--- a/encode.go
+++ b/encode.go
@@ -38,6 +38,7 @@ type Encoder struct {
 	anchorNameMap              map[string]struct{}
 	anchorCallback             func(*ast.AnchorNode, interface{}) error
 	customMarshalerMap         map[reflect.Type]func(interface{}) ([]byte, error)
+	omitEmpty                  bool
 	autoInt                    bool
 	useLiteralStyleIfMultiline bool
 	commentMap                 map[*Path][]*Comment
@@ -824,7 +825,7 @@ func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column
 		}
 		fieldValue := value.FieldByName(field.Name)
 		structField := structFieldMap[field.Name]
-		if structField.IsOmitEmpty && e.isZeroValue(fieldValue) {
+		if (e.omitEmpty || structField.IsOmitEmpty) && e.isZeroValue(fieldValue) {
 			// omit encoding
 			continue
 		}
diff --git a/option.go b/option.go
index 7dcd2ba..3849249 100644
--- a/option.go
+++ b/option.go
@@ -215,6 +215,15 @@ func AutoInt() EncodeOption {
 	}
 }
 
+// OmitEmpty forces the encoder to assume an `omitempty` struct tag is
+// set on all the fields. See `Marshal` commentary for the `omitempty` tag logic.
+func OmitEmpty() EncodeOption {
+	return func(e *Encoder) error {
+		e.omitEmpty = true
+		return nil
+	}
+}
+
 // CommentPosition type of the position for comment.
 type CommentPosition int
 


Test patch:
diff --git a/encode_test.go b/encode_test.go
index a82988f..2eba495 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -465,7 +465,7 @@ func TestEncoder(t *testing.T) {
 			nil,
 		},
 
-		// Conditional flag
+		// Omitempty flag.
 		{
 			"a: 1\n",
 			struct {
@@ -482,7 +482,6 @@ func TestEncoder(t *testing.T) {
 			}{0, 0},
 			nil,
 		},
-
 		{
 			"a:\n  \"y\": \"\"\n",
 			struct {
@@ -496,7 +495,6 @@ func TestEncoder(t *testing.T) {
 			}{}},
 			nil,
 		},
-
 		{
 			"a: {}\n",
 			struct {
@@ -510,7 +508,6 @@ func TestEncoder(t *testing.T) {
 			}{}},
 			nil,
 		},
-
 		{
 			"a: {x: 1}\n",
 			struct {
@@ -518,7 +515,6 @@ func TestEncoder(t *testing.T) {
 			}{&struct{ X, y int }{1, 2}},
 			nil,
 		},
-
 		{
 			"{}\n",
 			struct {
@@ -526,7 +522,6 @@ func TestEncoder(t *testing.T) {
 			}{nil},
 			nil,
 		},
-
 		{
 			"a: {x: 0}\n",
 			struct {
@@ -534,7 +529,6 @@ func TestEncoder(t *testing.T) {
 			}{&struct{ X, y int }{}},
 			nil,
 		},
-
 		{
 			"a: {x: 1}\n",
 			struct {
@@ -567,8 +561,29 @@ func TestEncoder(t *testing.T) {
 			},
 			nil,
 		},
+		// OmitEmpty global option.
+		{
+			"a: 1\n",
+			struct {
+				A int
+				B int `yaml:"b,omitempty"`
+			}{1, 0},
+			[]yaml.EncodeOption{
+				yaml.OmitEmpty(),
+			},
+		},
+		{
+			"{}\n",
+			struct {
+				A int
+				B int `yaml:"b,omitempty"`
+			}{0, 0},
+			[]yaml.EncodeOption{
+				yaml.OmitEmpty(),
+			},
+		},
 
-		// Flow flag
+		// Flow flag.
 		{
 			"a: [1, 2]\n",
 			struct {