Repository: go
Issue: encoding/xml: cannot unmarshal into without struct tags
Labels: NeedsInvestigation

### Go version

go version go1.21.6 linux/amd64

### Output of `go env` in your module/workspace:

```shell
GO111MODULE=''
GOARCH='amd64'
GOBIN='/home/daenney/.local/bin'
GOCACHE='/home/daenney/.cache/go-build'
GOENV='/home/daenney/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/daenney/.cache/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/daenney/.cache/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/go'
GOSUMDB='sum.golang.org'
GOTMPDIR='/run/user/1000'
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/lib/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.21.6'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/daenney/Development/github.com/daenney/xmldecode/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/run/user/1000/go-build4263511798=/tmp/go-build -gno-record-gcc-switches'
```


### What did you do?

I'm filing this one as a bug since the behaviour took me by surprise and wasn't obvious to me from the documentation on [encoding/xml.Unmashal](https://pkg.go.dev/encoding/xml#Unmarshal).

I tried to decode a response into a struct where:
* The member I'm decoding into is a slice of structs
* The member doesn't have an xml tag set
* The slice members are structs, which has an `XMLName` set

```xml
<response>
  <info>
    <lastmodified>2024-01-14T10:53:21Z</lastmodified>
  </info>
  <a><id>1</id></a>
  <a><id>2</id></a>
</response>
```

```go
type A3 struct {
	XMLName xml.Name `xml:"a"`
	ID      string   `xml:"id"`
}

type Response3 struct {
	XMLName      xml.Name `xml:"response"`
	Data         []A3
	LastModified *time.Time `xml:"info>lastmodified"`
}
```

I've included the full code in a Gist, because unfortunately `go.dev/play` continuously returns server errors when using the "Share" button: https://gist.github.com/daenney/07e546e0c9d26e5ba183feadd3ba00c4

### What did you see happen?

Whether a single, or multiple `<a>` elements are present, `.Data` remains empty, whereas I expected it to accumulate into `.Data` given the `XMLName` is present on the slice member struct.

```
&{XMLName:{Space: Local:response} Data:[] LastModified:2024-01-14 10:53:21 +0000 UTC}
&{XMLName:{Space: Local:response} Data:[] LastModified:2024-01-14 10:53:21 +0000 UTC}
```

### What did you expect to see?

It feels like the following two things should be equivalent and behave the same:

```go
type A2 struct {
	ID string `xml:"id"`
}

type Response2 struct {
	XMLName      xml.Name   `xml:"response"`
	Data         []A2       `xml:"a"`
	LastModified *time.Time `xml:"info>lastmodified"`
}
```

```go
type A3 struct {
	XMLName xml.Name `xml:"a"`
	ID      string   `xml:"id"`
}

type Response3 struct {
	XMLName      xml.Name `xml:"response"`
	Data         []A3
	LastModified *time.Time `xml:"info>lastmodified"`
}
```

Clearly they're not since `[]A3` doesn't directly have a tag and it seems the implementation doesn't take the `XMLName` of the slice members into account. It's not obvious from the documentation that in the case of a slice either the member name or the struct tag needs to match the XML element name for the decoding to occur. Even if I create a named type like `type Datas []T`, I can't attach the right xml tag to it nor give it an `XMLName` member since it's not a struct.

This isn't typically a problem as you can always write the `xml:"a"` tag, until we throw generics in the mix. Many APIs have a envelope, like seen here, with stuff like an `info` or `error` member in their response and the data elements, where the data elements can be a different type based on the query/endpoint. It would be nice to be able to define something like:

```go
type Response[T Responses] struct {
	XMLName      xml.Name `xml:"response"`
	Data         []T
	LastModified *time.Time `xml:"info>lastmodified"`
}

type Responses interface {
	RespA | RespB
}

type RespA struct{}
type RespB struct{}
```

There is one, hacky, way to make this work:

```go
type Response4[T Responses4] struct {
	XMLName      xml.Name   `xml:"response"`
	Data         []T        `xml:",any"`
	LastModified *time.Time `xml:"info>lastmodified"`
}
```

Due to the `xml:",any"` we now get the intended behaviour. But this is only safe if the wrapping `Response` type defines all other fields upfront, otherwise different elements could try to accumulate into `Response.Data` and `Unmashal` would fail.