I'm looking for a way to use https://github.com/google/jsonapi as a JSON (un)serializer. To be able to have custom bindings would be great but even though I looked for way to manage this, I couldn't guess how.

My idea then was to use a middleware to achieve this.

  • set a custom "resource" value (a model) in the context by using a custom middleware
  • populate this model by unmarshaling the request body in a common/generic middleware

Here is what my I made to make it working:

In main.go

    // subscribers routing
    subscriber := resources.Subscriber{}
    subscribers := router.Group(subscriber.BasePath())
    {
        subscribers.Use(middlewares.JsonAPI(&subscriber)).POST("", handlers.CreateSubscriber)
    }

The JsonAPI middleware:


const RESOURCE_CONTEXT = "resource"

func JsonAPI(r resources.APIResource) gin.HandlerFunc {
    return func(c *gin.Context) {
        if err := jsonapi.UnmarshalPayload(c.Request.Body, r); err != nil {
            responder.ErrorResponse(c, http.StatusBadRequest, []error{errors.New("Invalid JSON")})
            c.Abort()
            return
        }
        c.Set(RESOURCE_CONTEXT, r)
        c.Next()
    }
}

Then in my handler I can get the unmarshaled struct doing that:

subscriber := c.MustGet(middlewares.RESOURCE_CONTEXT).(*resources.Subscriber)

I really like this pattern as it makes my deserializing logic reusable but I'm going to add my own logic for validation too and it does not make sense as Gin binding are supposed to be the right way.

Comment From: takanuva15

Just wanted to add some extra details since I found this issue from google and it's been 7 years: - PR #3391 has been opened to customize the entire marshal/unmarshal process in Gin - As an alternate solution to the above, you can also customize how json serialization occurs by defining a custom renderer and then passing that to the c.Render function. There is a detailed comment here. - Basically, instead of calling c.JSON, you would call c.Render(200, WithJSONv2(res)). (The implementation of WithJSONv2 is in the linked comment) - Note: This doesn't apply globally, so you would need to invoke it wherever you are returning a response