Skip to content

Conversation

@abdullah2993
Copy link

Adds a new configuration option json_tags_omit_empty which adds omitempty to JSON tags.

Closes #1098
Closes #1084

Related To:
#1132
#1087

Adds a new configuration option json_tags_omit_empty
@abdullah2993 abdullah2993 force-pushed the feat/omitempty-support branch from 2ee0f0c to 81dd475 Compare January 9, 2024 21:41
@flexagoon
Copy link

What's the progress on this? Is this PR just waiting for a review?

@gregoryjjb
Copy link

Thoughts on an option for setting omitempty on nullable fields only? Slightly more of a pain to implement since JSONTagName doesn't know the nullability of its field.

@j-houston
Copy link

Is there an updated status here? Seems like a good fix

@andradei
Copy link

I don't know if the quality of the code is good, but this feature looks valuable. Reusing the sqlc-generated structs would be great. If they can be extended in any way via config options like this one, it makes them versitile.

@masar3141
Copy link

I'd like to have this feature as well

@spa5k
Copy link

spa5k commented Sep 17, 2024

Any update on merging it? There does not seem to be any interaction from contributors in this PR

@JakubCzarlinski
Copy link

@kyleconroy could you please have a look over this PR?

@zcharym
Copy link

zcharym commented Sep 26, 2024

looking forward to this PR being closed👍🏽

@JakubCzarlinski
Copy link

For those waiting for this feature - a workaround is to run this after SQLC has done generating. A parse is performed of the given file and omitempty tags are added to non-ignored fields that have existing json tags.

The script was hacked up without much care so take it as a word of warning if it does not work for your edge case.

package main

import (
	"bytes"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"log"
	"os"
	"strings"
)

const filePath = "./src/data/models.go"

func main() {
	// Parse the source code
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
	if err != nil {
		log.Fatal(err)
	}

	// Modify the AST
	ast.Inspect(f, func(n ast.Node) bool {
		switch x := n.(type) {
		case *ast.StructType:
			for _, field := range x.Fields.List {
				if field.Tag == nil {
					continue
				}
				if field.Tag.Value == "" || field.Tag.Kind != token.STRING {
					continue
				}

				field.Tag.Value = modifyJSONTag(field.Tag.Value)
			}
		}
		return true
	})

	// Write the output back to the original file
	var buf bytes.Buffer
	err = format.Node(&buf, fset, f)
	if err != nil {
		log.Fatal(err)
	}
	outputFile, err := os.Create(filePath)
	if err != nil {
		log.Fatal(err)
	}
	defer outputFile.Close()
	_, err = outputFile.Write(buf.Bytes())
	if err != nil {
		log.Fatal(err)
	}
}

func modifyJSONTag(tagValue string) string {
	tagValue = strings.Trim(tagValue, "`")

	tags := strings.Split(tagValue, " ")
	var modifiedTags []string

	for _, tag := range tags {
		// Only modify JSON tags, leave others as they are.
		if !strings.HasPrefix(tag, "json:") {
			modifiedTags = append(modifiedTags, tag)
			continue
		}

		jsonQuoted := tag[5:]                        // Remove "json:" prefix
		jsonValue := strings.Trim(jsonQuoted, "\"")  // Remove quotes
		jsonOptions := strings.Split(jsonValue, ",") // Split options

		// Check if "omitempty" is already present
		hasOmitempty := false
		for _, opt := range jsonOptions {
			if opt == "omitempty" {
				hasOmitempty = true
				break
			}
		}

		// Add "omitempty" if not present and the field is not ignored
		if !hasOmitempty && jsonOptions[0] != "-" {
			jsonOptions = append(jsonOptions, "omitempty")
		}

		// Reconstruct the JSON tag
		newJSONTag := "json:\"" + strings.Join(jsonOptions, ",") + "\""
		modifiedTags = append(modifiedTags, newJSONTag)
	}

	// Reconstruct the full tag
	return "`" + strings.Join(modifiedTags, " ") + "`"
}
@kyleconroy kyleconroy self-assigned this Nov 25, 2024
@reVrost
Copy link

reVrost commented Jun 13, 2025

I am also interested in this! 👍

@nicholasss
Copy link

Interested in this. Is there anything I can do to move this forward?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet