Skip to content

Conversation

@takanuva15
Copy link
Contributor

@takanuva15 takanuva15 commented Mar 27, 2025

With this PR, users can now specify parser=encoding.TextUnmarshaler in their form/uri tag to enable automatic binding using the encoding.TextUnmarshaler interface from the Golang standard library. Since this new parser tag is 100% opt-in only, this is fully backwards-compatible with previous versions of gin.

Merging this PR will resolve a number of tickets regarding this functionality:

closes #4177
closes #4312 (please correct me if I'm wrong)


Sample code to run:

package main

import (
  "encoding"
  "strings"

  "github.com/gin-gonic/gin"
)

type Birthday string

func (b *Birthday) UnmarshalText(text []byte) error {
  *b = Birthday(strings.Replace(string(text), "-", "/", -1))
  return nil
}

var _ encoding.TextUnmarshaler = (*Birthday)(nil) //assert Birthday implements encoding.TextUnmarshaler

func main() {
  route := gin.Default()
  var request struct {
    Birthday         Birthday   `form:"birthday,parser=encoding.TextUnmarshaler"`
    Birthdays        []Birthday `form:"birthdays,parser=encoding.TextUnmarshaler" collection_format:"csv"`
    BirthdaysDefault []Birthday `form:"birthdaysDef,default=2020-09-01;2020-09-02,parser=encoding.TextUnmarshaler" collection_format:"csv"`
  }
  route.GET("/test", func(ctx *gin.Context) {
    _ = ctx.BindQuery(&request)
    ctx.JSON(200, request)
  })
  _ = route.Run(":8088")
}

Test it with:

curl 'localhost:8088/test?birthday=2000-01-01&birthdays=2000-01-01,2000-01-02'

Result

{"Birthday":"2000/01/01","Birthdays":["2000/01/01","2000/01/02"],"BirthdaysDefault":["2020/09/01","2020/09/02"]

@codecov
Copy link

codecov bot commented Mar 27, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.82%. Comparing base (3dc1cd6) to head (8a07c82).
⚠️ Report is 187 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4203      +/-   ##
==========================================
- Coverage   99.21%   98.82%   -0.40%     
==========================================
  Files          42       44       +2     
  Lines        3182     3484     +302     
==========================================
+ Hits         3157     3443     +286     
- Misses         17       30      +13     
- Partials        8       11       +3     
Flag Coverage Δ
?
--ldflags="-checklinkname=0" -tags sonic 98.81% <100.00%> (?)
-tags go_json 98.75% <100.00%> (?)
-tags nomsgpack 98.80% <100.00%> (?)
go-1.18 ?
go-1.19 ?
go-1.20 ?
go-1.21 ?
go-1.24 98.82% <100.00%> (?)
go-1.25 98.82% <100.00%> (?)
macos-latest 98.82% <100.00%> (-0.40%) ⬇️
ubuntu-latest 98.82% <100.00%> (-0.40%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@takanuva15 takanuva15 marked this pull request as ready for review March 27, 2025 18:30
@takanuva15
Copy link
Contributor Author

@appleboy ready for review. I verified locally that all my new changes are covered with the new tests I added. The linting failure is unrelated to my changes since I did not add any sprintf calls to my code

@appleboy appleboy requested a review from Copilot May 20, 2025 15:13
@appleboy appleboy added this to the v1.11 milestone May 20, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for encoding.TextUnmarshaler-based binding via a new parser=encoding.TextUnmarshaler tag option.

  • Introduce a parser option in form_mapping.go and implement trySetUsingParser
  • Update setByForm and related functions to invoke UnmarshalText when parser is set
  • Enhance documentation (docs/doc.md) with usage examples and notes

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
docs/doc.md Add new “Bind custom unmarshaler” section and notes for parser tag
binding/form_mapping.go Parse parser tag, implement trySetUsingParser, wire into setter
Comments suppressed due to low confidence (2)

binding/form_mapping.go:199

  • There are no unit tests covering the new parser=encoding.TextUnmarshaler behavior. Consider adding tests for simple values, slices, and default-value scenarios to prevent regressions.
func trySetUsingParser(val string, value reflect.Value, parser string) (isSet bool, err error) {

docs/doc.md:1054

  • The JSON example is missing a closing } at the end; add the trailing brace to make the output valid JSON.
{"Birthday":"2000/01/01","Birthdays":["2000/01/01","2000/01/02"],"BirthdaysDefault":["2020/09/01","2020/09/02"]

Comment on lines +261 to 265
if ok, err = trySetUsingParser(vs[0], value, opt.parser); ok {
return ok, err
} else if ok, err = trySetCustom(vs[0], value); ok {
return ok, err
Copy link

Copilot AI May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The pattern of invoking trySetUsingParser followed by trySetCustom is repeated in multiple branches. Extracting this into a small helper would reduce duplication and improve readability.

Suggested change
if ok, err = trySetUsingParser(vs[0], value, opt.parser); ok {
return ok, err
} else if ok, err = trySetCustom(vs[0], value); ok {
return ok, err
if ok, err = trySetValue(vs[0], value, opt); ok {
return ok, err

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only repeated once. Extracting it to a function will make this code even more difficult to understand than it already is

@appleboy
Copy link
Member

Please rebase the master branch. @takanuva15

@takanuva15
Copy link
Contributor Author

@appleboy done, just rebased it now

@takanuva15
Copy link
Contributor Author

I'm not sure why the coverage shows as dropped, but I just ran a full coverage report via

go test -cover -coverprofile=c.out ./... -coverpkg=./... \
      && go tool cover -html=c.out -o coverage.html

and it shows 100% coverage on form_mapping.go, which is the only file I modified.

image

@appleboy
Copy link
Member

@takanuva15 Please help to resolve the conflicts.

@takanuva15 takanuva15 force-pushed the master branch 5 times, most recently from d37dc58 to ae06b4d Compare May 27, 2025 13:42
@takanuva15
Copy link
Contributor Author

@takanuva15 Please help to resolve the conflicts.

done

@appleboy appleboy requested a review from Copilot May 28, 2025 01:37
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds support for the parser=encoding.TextUnmarshaler tag in form/URI binding so users can opt in to encoding.TextUnmarshaler-based decoding.

  • Updated docs with examples and behavior notes for the new parser tag.
  • Extended binding/form_mapping.go to parse a parser option, added trySetUsingParser, and wired it into slice, array, and scalar binding.
  • Broadened the error message for unsupported collection_format to list all valid formats.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
docs/doc.md Added UnmarshalText examples, notes on parser behavior
binding/form_mapping.go Introduced parser field in setOptions, trySetUsingParser, and updated binding logic
Comments suppressed due to low confidence (2)

binding/form_mapping.go:199

  • No unit tests cover the new parser=encoding.TextUnmarshaler path; please add tests for both successful and failure scenarios when the target type does and does not implement TextUnmarshaler.
func trySetUsingParser(val string, value reflect.Value, parser string) (isSet bool, err error) {

docs/doc.md:1106

  • The JSON example is missing a closing }. Please add the final brace to make the example valid JSON.
{"Birthday":"2000/01/01","Birthdays":["2000/01/01","2000/01/02"],"BirthdaysDefault":["2020/09/01","2020/09/02"]

Comment on lines +261 to 265
if ok, err = trySetUsingParser(vs[0], value, opt.parser); ok {
return ok, err
} else if ok, err = trySetCustom(vs[0], value); ok {
return ok, err
Copy link

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The pattern of calling trySetUsingParser and then trySetCustom is repeated in multiple branches. Consider unifying this sequence into a helper to reduce duplication and improve clarity.

Suggested change
if ok, err = trySetUsingParser(vs[0], value, opt.parser); ok {
return ok, err
} else if ok, err = trySetCustom(vs[0], value); ok {
return ok, err
if ok, err = trySetValue(vs[0], value, opt.parser); ok {
return ok, err

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

@takanuva15 takanuva15 May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only repeated once. Extracting it to a function will make this code even more difficult to understand than it already is.

Other comments;

  • test coverage: form_mapping.go is at 100% test coverage, which is the only file I edited
  • doc json mistake: fixed it and repushed

@appleboy
Copy link
Member

I will take it.

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

Labels

Projects

None yet

2 participants