Home > Software engineering >  Golang Gin Binding Request body XML to Slice
Golang Gin Binding Request body XML to Slice

Time:10-29

I am having an issue with the default binding in Gin. I have an incoming request where the body is multiple Entity objects like so:

<Entity>
  <Name>Name One here</Name>
  ...
</Entity>
<Entity>
  <Name>Name Two here</Name>
  ...
</Entity>

My goal is to map it to a corresponding slice. So the struct for the desired object is like so:

type Entity struct {
  XMLName xml.Name `bson:"-" json:"-" xml:"Entity"`
  Name   string    `bson:"name,omitempty" json:",omitempty" xml:",omitempty"`
  ...
}

The problem I'm experiencing is that only one of the supplied objects is ever mapped into the slice, regardless of how many are passed in the request body. Note that the JSON version of the request parses correctly.

[
 {
   Name: "Name One",
   ...
 },
 {
   Name: "Name Two",
   ...
 }
]

I have a struct to model request structure

type ApplicationRequest struct {
  XMLName      xml.Name `bson:"-" xml:"Entities"`
  Entities     []Entity `binding:"required" xml:"Entity"`
  ParameterOne bool
  ...
}

So now within the controller function, I handle the binding like this:

func RequestHandler() gin.HandlerFunc {
  return func(c *gin.Context) {
    var request ApplicationRequest
    if err := c.Bind(&request.Entities); err != nil {
        responseFunction(http.StatusBadRequest, ..., Message: err.Error()})
        return
    }
    // At this point, the request.Entities slice has ONE element, never more than one
  }
}

Note I'm using the gin context.Bind(...) function because it handles the parsing of JSON or XML implicitly, and works for all other scenarios that I need.

Hopefully this provides enough context, any help would be greatly appreciated! Thanks!

CodePudding user response:

it is not a gin problem: unmarshal-xml-array-in-golang-only-getting-the-first-element

follow is two way to deal it:

  • add a root node just like @zangw

  • change the bind method by 'for' github.com\gin-gonic\[email protected]\binding\xml.go line 28 func decodeXML

from

func decodeXML(r io.Reader, obj any) error {
    decoder := xml.NewDecoder(r)
    if err := decoder.Decode(obj); err != nil {
        return err
    }
    return validate(obj)
}

to

func decodeXML(r io.Reader, obj any) error {
    decoder := xml.NewDecoder(r)
    for  {
        if err := decoder.Decode(obj); err != nil {
            if err == io.EOF{
                break
            }
            return err
        }
    }
    return validate(obj)
}

  • Related