package xml import ( "bytes" "encoding/xml" "strings" ) // ExtractElement finds the first occurrence of the named element in the XML // and returns it as raw bytes, including the element's opening and closing // tags. // // This is used to extract both success and error responses from the protocol // XML that wraps them. // // For example for awsquery, with an operation output shape named // XmlListsResult: // // // // foo // ... // // // // ExtractElement(payload, "XmlListsResult") yields: // // foo... // // ExtractElement will forward the io.EOF returned by the underlying decoder // if the target element is not found, which the caller can index on if // they're looking for an optional element. func ExtractElement(payload []byte, name string) ([]byte, error) { dec := xml.NewDecoder(bytes.NewReader(payload)) for { tok, err := dec.Token() if err != nil { return nil, err } start, ok := tok.(xml.StartElement) if !ok || !strings.EqualFold(start.Name.Local, name) { continue } var buf bytes.Buffer enc := xml.NewEncoder(&buf) if err := enc.EncodeToken(start); err != nil { return nil, err } if err := enc.Flush(); err != nil { return nil, err } depth := 1 for depth > 0 { tok, err := dec.Token() if err != nil { return nil, err } if err := enc.EncodeToken(tok); err != nil { return nil, err } switch tok.(type) { case xml.StartElement: depth++ case xml.EndElement: depth-- } } if err := enc.Flush(); err != nil { return nil, err } return buf.Bytes(), nil } }