直接将子元素的属性解析为Go结构

In parsing XML with Go, how can I read a nested element's attribute directly into my struct?

My XML looks like this below. It is part of the OpenStreetMap format:

<way id="123" >
        <nd ref="101"/>
        <!-- Lots of nd elements repeated -->
        <nd ref="109"/>
</way>

I have

type Way struct {
    Nodes []NodeRef `xml:"nd"`
}

with

type NodeRef struct {
    Ref int `xml:"ref,attr"`
}

but I would like to be able to do

type Way struct {
    Nodes []int `???`
}

directly.

The documentation on Unmarshalling didn't help me. I've tried using xml:"nd>ref,attr" but that fails with "chain not valid with attr flag".

Please see the below example code. Run the code in Go Playground

package main

import (
    "encoding/xml"
    "fmt"
    "io"
    "os"
    "strings"
)

func main() {
    data := `
        <way id="5090250" >
        <nd ref="822403"/>
        <nd ref="21533912"/>
        <nd ref="821601"/>
        <nd ref="21533910"/>
        <nd ref="135791608"/>
        <nd ref="333725784"/>
        <nd ref="333725781"/>
        <nd ref="333725774"/>
        <nd ref="333725776"/>
        <nd ref="823771"/>
      </way>
    `

    r := strings.NewReader(data)
    way, err := ReadWay(r)
    if err != nil {
        fmt.Println("Could not read", err)
        os.Exit(1)
    }

    fmt.Println(way)
}

// I'd like to get rid of this nested struct.
type NodeRef struct {
    Ref int `xml:"ref,attr"`
}

type Way struct {
    ID int `xml:"id,attr"`
    // How can I write all <nd ref="123"/> directly into Nodes []int?
    Nodes []NodeRef `xml:"nd"`
}

func ReadWay(reader io.Reader) (Way, error) {
    var way Way
    if err := xml.NewDecoder(reader).Decode(&way); err != nil {
        return way, err // Why can't I return nil, err?
    }
    return way, nil
}

In short, you can't do that directly. The common pattern to bypass the XML structure is to implement the xml.Unmarshaler interface. Here is an example:

type Way struct {
    ID    int 
    Nodes []int
}

func (w *Way) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var payload struct {
        ID    int `xml:"id,attr"`
        Nodes []struct {
            Ref int `xml:"ref,attr"`
        } `xml:"nd"`
    }   

    err := d.DecodeElement(&payload, &start)
    if err != nil {
        return err 
    }   

    w.ID = payload.ID
    w.Nodes = make([]int, 0, len(payload.Nodes))

    for _, node := range payload.Nodes {
        w.Nodes = append(w.Nodes, node.Ref)
    }   

    return nil 
}

Here, the payload variable is modeled after your XML data, while the Way struct is modeled as you want it to be. Once the payload is decoded, you can use it to initialize the Way variable as you want…

Side note: // Why can't I return nil, err?

You can't return nil because Way isn't either a pointer or an interface, thus nil isn't a valid value for it.