Skip to content
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

Is there a to set a custom JSON serializer/deserializer? #4112

Open
Scythemen opened this issue Dec 3, 2024 · 1 comment
Open

Is there a to set a custom JSON serializer/deserializer? #4112

Scythemen opened this issue Dec 3, 2024 · 1 comment

Comments

@Scythemen
Copy link

Is there a way to set a custom JSON serializer?

Sometimes, I need to communicate with other services, especially legacy ones.
I require all keys in the JSON response to be formatted in snake_case, lower case, or upper case.
Is there a way to achieve this?
e.g.

gin.SetJson(&customeJsonSerializer{})
// or
r := gin.Default()
r.SetJson(&customeJsonSerializer{})
@takanuva15
Copy link
Contributor

Hey I was wondering the same exact thing since I was trying to serialize all my time.Time fields as unix millisecond integers rather than string dates. There is an open PR #3391 which would enable us to fully customize the Marshal/Unmarshal methods that gin uses for json conversion. It might get merged and released for gin v1.11, but that's not definitive.

In the meantime though, gin has a render.Render interface that you can manipulate to serialize the json any way you like.

First, define your custom render function. In my case, I wanted to use the new experimental json/v2 package for marshalling so I could take advantage of their new json options like omitzero and format:unixmilli:

// this code is a copy-paste of the existing render.JSON struct except for the json package being used for marshalling

import (
	"net/http"

	"github.com/gin-gonic/gin/render"
	"github.com/go-json-experiment/json/v1"
)

type JSONv2 struct {
	render.JSON
}

func (r JSONv2) Render(w http.ResponseWriter) error {
	writeContentType(w, []string{"application/json; charset=utf-8"})
	jsonBytes, err := json.Marshal(r.Data)
	if err != nil {
		return err
	}
	_, err = w.Write(jsonBytes)
	return err
}

func writeContentType(w http.ResponseWriter, value []string) {
	header := w.Header()
	if val := header["Content-Type"]; len(val) == 0 {
		header["Content-Type"] = value
	}
}

I also added a utility method to make the JSONv2 struct easier to use in my gin endpoints:

func WithJSONv2(data any) JSONv2 {
	return JSONv2{render.JSON{Data: data}}
}

Then in your gin code, you just need to invoke the function like so:

c.Render(200, WithJSONv2(res))

And that's it!

Note that this is not as elegant as the open PR since you will need to invoke WithJSONv2 in every endpoint where you want to use your custom serializer. But hopefully whenever the PR gets merged, you can run a quick regex replace on your codebase to convert all these usages back to the normal c.JSON(200, res) that's standard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants