|
Packit Service |
4d2de5 |
package xmlutil
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
import (
|
|
Packit Service |
4d2de5 |
"bytes"
|
|
Packit Service |
4d2de5 |
"encoding/base64"
|
|
Packit Service |
4d2de5 |
"encoding/xml"
|
|
Packit Service |
4d2de5 |
"fmt"
|
|
Packit Service |
4d2de5 |
"io"
|
|
Packit Service |
4d2de5 |
"reflect"
|
|
Packit Service |
4d2de5 |
"strconv"
|
|
Packit Service |
4d2de5 |
"strings"
|
|
Packit Service |
4d2de5 |
"time"
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
Packit Service |
4d2de5 |
"github.com/aws/aws-sdk-go/private/protocol"
|
|
Packit Service |
4d2de5 |
)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// UnmarshalXMLError unmarshals the XML error from the stream into the value
|
|
Packit Service |
4d2de5 |
// type specified. The value must be a pointer. If the message fails to
|
|
Packit Service |
4d2de5 |
// unmarshal, the message content will be included in the returned error as a
|
|
Packit Service |
4d2de5 |
// awserr.UnmarshalError.
|
|
Packit Service |
4d2de5 |
func UnmarshalXMLError(v interface{}, stream io.Reader) error {
|
|
Packit Service |
4d2de5 |
var errBuf bytes.Buffer
|
|
Packit Service |
4d2de5 |
body := io.TeeReader(stream, &errBuf)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
err := xml.NewDecoder(body).Decode(v)
|
|
Packit Service |
4d2de5 |
if err != nil && err != io.EOF {
|
|
Packit Service |
4d2de5 |
return awserr.NewUnmarshalError(err,
|
|
Packit Service |
4d2de5 |
"failed to unmarshal error message", errBuf.Bytes())
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// UnmarshalXML deserializes an xml.Decoder into the container v. V
|
|
Packit Service |
4d2de5 |
// needs to match the shape of the XML expected to be decoded.
|
|
Packit Service |
4d2de5 |
// If the shape doesn't match unmarshaling will fail.
|
|
Packit Service |
4d2de5 |
func UnmarshalXML(v interface{}, d *xml.Decoder, wrapper string) error {
|
|
Packit Service |
4d2de5 |
n, err := XMLToStruct(d, nil)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
if n.Children != nil {
|
|
Packit Service |
4d2de5 |
for _, root := range n.Children {
|
|
Packit Service |
4d2de5 |
for _, c := range root {
|
|
Packit Service |
4d2de5 |
if wrappedChild, ok := c.Children[wrapper]; ok {
|
|
Packit Service |
4d2de5 |
c = wrappedChild[0] // pull out wrapped element
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
err = parse(reflect.ValueOf(v), c, "")
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
if err == io.EOF {
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// parse deserializes any value from the XMLNode. The type tag is used to infer the type, or reflect
|
|
Packit Service |
4d2de5 |
// will be used to determine the type from r.
|
|
Packit Service |
4d2de5 |
func parse(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
|
Packit Service |
4d2de5 |
rtype := r.Type()
|
|
Packit Service |
4d2de5 |
if rtype.Kind() == reflect.Ptr {
|
|
Packit Service |
4d2de5 |
rtype = rtype.Elem() // check kind of actual element type
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
t := tag.Get("type")
|
|
Packit Service |
4d2de5 |
if t == "" {
|
|
Packit Service |
4d2de5 |
switch rtype.Kind() {
|
|
Packit Service |
4d2de5 |
case reflect.Struct:
|
|
Packit Service |
4d2de5 |
// also it can't be a time object
|
|
Packit Service |
4d2de5 |
if _, ok := r.Interface().(*time.Time); !ok {
|
|
Packit Service |
4d2de5 |
t = "structure"
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
case reflect.Slice:
|
|
Packit Service |
4d2de5 |
// also it can't be a byte slice
|
|
Packit Service |
4d2de5 |
if _, ok := r.Interface().([]byte); !ok {
|
|
Packit Service |
4d2de5 |
t = "list"
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
case reflect.Map:
|
|
Packit Service |
4d2de5 |
t = "map"
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
switch t {
|
|
Packit Service |
4d2de5 |
case "structure":
|
|
Packit Service |
4d2de5 |
if field, ok := rtype.FieldByName("_"); ok {
|
|
Packit Service |
4d2de5 |
tag = field.Tag
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return parseStruct(r, node, tag)
|
|
Packit Service |
4d2de5 |
case "list":
|
|
Packit Service |
4d2de5 |
return parseList(r, node, tag)
|
|
Packit Service |
4d2de5 |
case "map":
|
|
Packit Service |
4d2de5 |
return parseMap(r, node, tag)
|
|
Packit Service |
4d2de5 |
default:
|
|
Packit Service |
4d2de5 |
return parseScalar(r, node, tag)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// parseStruct deserializes a structure and its fields from an XMLNode. Any nested
|
|
Packit Service |
4d2de5 |
// types in the structure will also be deserialized.
|
|
Packit Service |
4d2de5 |
func parseStruct(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
|
Packit Service |
4d2de5 |
t := r.Type()
|
|
Packit Service |
4d2de5 |
if r.Kind() == reflect.Ptr {
|
|
Packit Service |
4d2de5 |
if r.IsNil() { // create the structure if it's nil
|
|
Packit Service |
4d2de5 |
s := reflect.New(r.Type().Elem())
|
|
Packit Service |
4d2de5 |
r.Set(s)
|
|
Packit Service |
4d2de5 |
r = s
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
r = r.Elem()
|
|
Packit Service |
4d2de5 |
t = t.Elem()
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// unwrap any payloads
|
|
Packit Service |
4d2de5 |
if payload := tag.Get("payload"); payload != "" {
|
|
Packit Service |
4d2de5 |
field, _ := t.FieldByName(payload)
|
|
Packit Service |
4d2de5 |
return parseStruct(r.FieldByName(payload), node, field.Tag)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
for i := 0; i < t.NumField(); i++ {
|
|
Packit Service |
4d2de5 |
field := t.Field(i)
|
|
Packit Service |
4d2de5 |
if c := field.Name[0:1]; strings.ToLower(c) == c {
|
|
Packit Service |
4d2de5 |
continue // ignore unexported fields
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// figure out what this field is called
|
|
Packit Service |
4d2de5 |
name := field.Name
|
|
Packit Service |
4d2de5 |
if field.Tag.Get("flattened") != "" && field.Tag.Get("locationNameList") != "" {
|
|
Packit Service |
4d2de5 |
name = field.Tag.Get("locationNameList")
|
|
Packit Service |
4d2de5 |
} else if locName := field.Tag.Get("locationName"); locName != "" {
|
|
Packit Service |
4d2de5 |
name = locName
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// try to find the field by name in elements
|
|
Packit Service |
4d2de5 |
elems := node.Children[name]
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if elems == nil { // try to find the field in attributes
|
|
Packit Service |
4d2de5 |
if val, ok := node.findElem(name); ok {
|
|
Packit Service |
4d2de5 |
elems = []*XMLNode{{Text: val}}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
member := r.FieldByName(field.Name)
|
|
Packit Service |
4d2de5 |
for _, elem := range elems {
|
|
Packit Service |
4d2de5 |
err := parse(member, elem, field.Tag)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// parseList deserializes a list of values from an XML node. Each list entry
|
|
Packit Service |
4d2de5 |
// will also be deserialized.
|
|
Packit Service |
4d2de5 |
func parseList(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
|
Packit Service |
4d2de5 |
t := r.Type()
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if tag.Get("flattened") == "" { // look at all item entries
|
|
Packit Service |
4d2de5 |
mname := "member"
|
|
Packit Service |
4d2de5 |
if name := tag.Get("locationNameList"); name != "" {
|
|
Packit Service |
4d2de5 |
mname = name
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if Children, ok := node.Children[mname]; ok {
|
|
Packit Service |
4d2de5 |
if r.IsNil() {
|
|
Packit Service |
4d2de5 |
r.Set(reflect.MakeSlice(t, len(Children), len(Children)))
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
for i, c := range Children {
|
|
Packit Service |
4d2de5 |
err := parse(r.Index(i), c, "")
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
} else { // flattened list means this is a single element
|
|
Packit Service |
4d2de5 |
if r.IsNil() {
|
|
Packit Service |
4d2de5 |
r.Set(reflect.MakeSlice(t, 0, 0))
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
childR := reflect.Zero(t.Elem())
|
|
Packit Service |
4d2de5 |
r.Set(reflect.Append(r, childR))
|
|
Packit Service |
4d2de5 |
err := parse(r.Index(r.Len()-1), node, "")
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// parseMap deserializes a map from an XMLNode. The direct children of the XMLNode
|
|
Packit Service |
4d2de5 |
// will also be deserialized as map entries.
|
|
Packit Service |
4d2de5 |
func parseMap(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
|
Packit Service |
4d2de5 |
if r.IsNil() {
|
|
Packit Service |
4d2de5 |
r.Set(reflect.MakeMap(r.Type()))
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if tag.Get("flattened") == "" { // look at all child entries
|
|
Packit Service |
4d2de5 |
for _, entry := range node.Children["entry"] {
|
|
Packit Service |
4d2de5 |
parseMapEntry(r, entry, tag)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
} else { // this element is itself an entry
|
|
Packit Service |
4d2de5 |
parseMapEntry(r, node, tag)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// parseMapEntry deserializes a map entry from a XML node.
|
|
Packit Service |
4d2de5 |
func parseMapEntry(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
|
Packit Service |
4d2de5 |
kname, vname := "key", "value"
|
|
Packit Service |
4d2de5 |
if n := tag.Get("locationNameKey"); n != "" {
|
|
Packit Service |
4d2de5 |
kname = n
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
if n := tag.Get("locationNameValue"); n != "" {
|
|
Packit Service |
4d2de5 |
vname = n
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
keys, ok := node.Children[kname]
|
|
Packit Service |
4d2de5 |
values := node.Children[vname]
|
|
Packit Service |
4d2de5 |
if ok {
|
|
Packit Service |
4d2de5 |
for i, key := range keys {
|
|
Packit Service |
4d2de5 |
keyR := reflect.ValueOf(key.Text)
|
|
Packit Service |
4d2de5 |
value := values[i]
|
|
Packit Service |
4d2de5 |
valueR := reflect.New(r.Type().Elem()).Elem()
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
parse(valueR, value, "")
|
|
Packit Service |
4d2de5 |
r.SetMapIndex(keyR, valueR)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// parseScaller deserializes an XMLNode value into a concrete type based on the
|
|
Packit Service |
4d2de5 |
// interface type of r.
|
|
Packit Service |
4d2de5 |
//
|
|
Packit Service |
4d2de5 |
// Error is returned if the deserialization fails due to invalid type conversion,
|
|
Packit Service |
4d2de5 |
// or unsupported interface type.
|
|
Packit Service |
4d2de5 |
func parseScalar(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
|
Packit Service |
4d2de5 |
switch r.Interface().(type) {
|
|
Packit Service |
4d2de5 |
case *string:
|
|
Packit Service |
4d2de5 |
r.Set(reflect.ValueOf(&node.Text))
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
case []byte:
|
|
Packit Service |
4d2de5 |
b, err := base64.StdEncoding.DecodeString(node.Text)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
r.Set(reflect.ValueOf(b))
|
|
Packit Service |
4d2de5 |
case *bool:
|
|
Packit Service |
4d2de5 |
v, err := strconv.ParseBool(node.Text)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
r.Set(reflect.ValueOf(&v))
|
|
Packit Service |
4d2de5 |
case *int64:
|
|
Packit Service |
4d2de5 |
v, err := strconv.ParseInt(node.Text, 10, 64)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
r.Set(reflect.ValueOf(&v))
|
|
Packit Service |
4d2de5 |
case *float64:
|
|
Packit Service |
4d2de5 |
v, err := strconv.ParseFloat(node.Text, 64)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
r.Set(reflect.ValueOf(&v))
|
|
Packit Service |
4d2de5 |
case *time.Time:
|
|
Packit Service |
4d2de5 |
format := tag.Get("timestampFormat")
|
|
Packit Service |
4d2de5 |
if len(format) == 0 {
|
|
Packit Service |
4d2de5 |
format = protocol.ISO8601TimeFormatName
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
t, err := protocol.ParseTime(format, node.Text)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
r.Set(reflect.ValueOf(&t))
|
|
Packit Service |
4d2de5 |
default:
|
|
Packit Service |
4d2de5 |
return fmt.Errorf("unsupported value: %v (%s)", r.Interface(), r.Type())
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|