Skip to content

Add support for omitempty in JSON tags #3117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
10 participants