如何在GO中解组动态XML

I try to parse XML using Go, which is used to exchange data with another system. I am aware how parsing with the xml.Unmarshal function works in principle. However, in the protocol the child element of the root element <PROTO> vary (see the examples below). For the child elements (REQUEST.DATA, REQUEST.ACL, REQUEST.NAC, ... - far more than twenty different) I have defined structs with xml:...-syntax so far - the parsing of these single child elements works.

But, is there a nice way to parse the whole message as a struct, where one varible type depends on the child element? For example with using an interface?

Example of possible XML messages:

<PROTO
   version="1.00">
  <REQUEST.DATA>
    <DATA-L>Some information</DATA-L>
  </REQUEST.DATA>
</PROTO>
<PROTO
   version="1.00">
  <REQUEST.ACK>
    <ACK-ID>1</ACK-ID>
  <REQUEST.ACK>
</PROTO>

Snippets from my Go application:

// XML: REQUEST.DATA
type DataRequest struct {
    LData string `xml:"DATA-L"`
}

// XML: REQUEST.ACK
type AckRequest struct {
    AckId int `xml:"ACK-ID"`
}

// XML: PROTO  <============= ??
type Request struct {
    Version float32  `xml:"version,attr"`
    RemoteRequest {AckRequest, DataRequest, ...} ????
}

func main() {
    message := `<PROTO version="1.00"><REQUEST.ACK><ACK-ID>1</ACK-ID><REQUEST.ACK></PROTO>`
    data := `<REQUEST.ACK><ACK-ID>1</ACK-ID><REQUEST.ACK>`
    doc := &AckRequest{}
    err := xml.Unmarshal([]byte(data), &doc)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }
    fmt.Printf("data %+v", doc)
}

Instead of just parsing the child element data I want to parse the whole document message (<PROTO> involved). And want have a structure that contains the information of the respective child element.

You can embed the DataRequest and AckRequest as pointers in the Request struct. This way you can check later if they are nil or not.

// XML: REQUEST.DATA
type DataRequest struct {
    LData string `xml:"DATA-L"`
}

// XML: REQUEST.ACK
type AckRequest struct {
    AckId int `xml:"ACK-ID"`
}

type Request struct {
    Version float32      `xml:"version,attr"`
    Ack     *AckRequest  `xml:"REQUEST.ACK"`
    Data    *DataRequest `xml:"REQUEST.DATA"`
}

func main() {
    message := `<PROTO version="1.00"><REQUEST.ACK><ACK-ID>1</ACK-ID></REQUEST.ACK></PROTO>`
    proto := &Request{}
    err := xml.Unmarshal([]byte(message), &proto)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }
    // if proto.Ack == nil {
    //   fmt.Println("Ack is nil")
    // }
    fmt.Printf("data %+v
", proto)
    fmt.Printf("data %+v
", proto.Ack)
}

data &{Version:1 Ack:0xc0000b6050 Data:}

data &{AckId:1}

You can also implement your own decoder and do dynamic type switching https://stackoverflow.com/a/33517875/1199408.