diff --git a/private/protocol/xml/xmlutil/unmarshal.go b/private/protocol/xml/xmlutil/unmarshal.go index fabb82f8816..64b6ddd3e18 100644 --- a/private/protocol/xml/xmlutil/unmarshal.go +++ b/private/protocol/xml/xmlutil/unmarshal.go @@ -111,11 +111,8 @@ func parseStruct(r reflect.Value, node *XMLNode, tag reflect.StructTag) error { elems := node.Children[name] if elems == nil { // try to find the field in attributes - for _, a := range node.Attr { - if name == strings.Join([]string{a.Name.Space, a.Name.Local}, ":") { - // turn this into a text node for de-serializing - elems = []*XMLNode{{Text: a.Value}} - } + if val, ok := node.findElem(name); ok { + elems = []*XMLNode{{Text: val}} } } diff --git a/private/protocol/xml/xmlutil/xml_to_struct.go b/private/protocol/xml/xmlutil/xml_to_struct.go index 8e0337bd33a..3112512a210 100644 --- a/private/protocol/xml/xmlutil/xml_to_struct.go +++ b/private/protocol/xml/xmlutil/xml_to_struct.go @@ -2,6 +2,7 @@ package xmlutil import ( "encoding/xml" + "fmt" "io" "sort" ) @@ -12,6 +13,9 @@ type XMLNode struct { Children map[string][]*XMLNode `json:",omitempty"` Text string `json:",omitempty"` Attr []xml.Attr `json:",omitempty"` + + namespaces map[string]string + parent *XMLNode } // NewXMLElement returns a pointer to a new XMLNode initialized to default values. @@ -59,41 +63,52 @@ func XMLToStruct(d *xml.Decoder, s *xml.StartElement) (*XMLNode, error) { slice = []*XMLNode{} } node, e := XMLToStruct(d, &el) + out.findNamespaces() if e != nil { return out, e } node.Name = typed.Name - node.Attr = out.Attr - node = adaptNode(node) + node.findNamespaces() + tempOut := *out + // Save into a temp variable, simply because out gets squashed during + // loop iterations + node.parent = &tempOut slice = append(slice, node) out.Children[name] = slice case xml.EndElement: if s != nil && s.Name.Local == typed.Name.Local { // matching end token return out, nil } + out = &XMLNode{} } } return out, nil } -func adaptNode(node *XMLNode) *XMLNode { +func (n *XMLNode) findNamespaces() { ns := map[string]string{} - for _, a := range node.Attr { + for _, a := range n.Attr { if a.Name.Space == "xmlns" { ns[a.Value] = a.Name.Local - break } } - for i, a := range node.Attr { - if a.Name.Space == "xmlns" { - continue - } - if v, ok := ns[node.Attr[i].Name.Space]; ok { - node.Attr[i].Name.Space = v + n.namespaces = ns +} + +func (n *XMLNode) findElem(name string) (string, bool) { + for node := n; node != nil; node = node.parent { + for _, a := range node.Attr { + namespace := a.Name.Space + if v, ok := node.namespaces[namespace]; ok { + namespace = v + } + if name == fmt.Sprintf("%s:%s", namespace, a.Name.Local) { + return a.Value, true + } } } - return node + return "", false } // StructToXML writes an XMLNode to a xml.Encoder as tokens.