gin icon indicating copy to clipboard operation
gin copied to clipboard

Other Serialization formats - Amazon ION

Open BigB84 opened this issue 3 years ago • 1 comments

Hi,

Thanks for creating gin, it's awesome!

I'm building RESTFUL API but I'd like use serialization format other than available here. I chose Amazon ION. There is an official go library And even curl-like toll supporting it, see here how it sets application/ion header.

Is it possible?

BigB84 avatar Dec 31 '22 01:12 BigB84

You could write your own binding and render if you want. I'm by no means an expert and I never used ion before, but this is how I would tackle it:

🐞 I think there is still a bug in the binding code. Or I use ion wrong.

main

type Person struct {
	Name string `ion:"name"`
	Age  int    `ion:"age"`
}

func main() {
	r := gin.Default()

	r.GET("/testing", func(c *gin.Context) {
		person := Person{Name: "Jane Doe", Age: 44}
		c.Render(http.StatusOK, Ion(person))
	})

	r.POST("/testing", func(c *gin.Context) {
		var input Person
		if err := c.MustBindWith(&input, IonBinding); err != nil {
			return
		}
		c.String(http.StatusOK, "%s (%d)", input.Name, input.Age)
	})

	if err := r.Run(":8080"); err != nil {
		log.Fatalln(err)
	}
}

binding

type IonBinding struct{}

func (IonBinding) Name() string {
	return "ion"
}

func (IonBinding) Bind(req *http.Request, obj any) error {
	if req == nil || req.Body == nil {
		return errors.New("invalid request")
	}
	return decodeIon(req.Body, obj)
}

func (IonBinding) BindBody(body []byte, obj any) error {
	return decodeIon(bytes.NewReader(body), obj)
}

func decodeIon(r io.Reader, obj any) error {
	reader := ion.NewReader(r)
	decoder := ion.NewDecoder(reader)

	if err := decoder.DecodeTo(obj); err != nil {
		return err
	}

	if binding.Validator == nil {
		return nil
	}
	return binding.Validator.ValidateStruct(obj)
}

render

type IonRender struct {
	Data any
}

func Ion(data any) IonRender {
	return IonRender{Data: data}
}

func (ir IonRender) Render(w http.ResponseWriter) error {
	writeIonContentType(w)

	bytes, err := ion.MarshalBinary(ir.Data)
	if err != nil {
		return err
	}
	_, err = w.Write(bytes)
	return err
}

func (IonRender) WriteContentType(w http.ResponseWriter) {
	writeIonContentType(w)
}

func writeIonContentType(w http.ResponseWriter) {
	header := w.Header()
	if val := header["Content-Type"]; len(val) == 0 {
		header["Content-Type"] = []string{"application/ion"}
	}
}

pscheid92 avatar May 05 '23 12:05 pscheid92