github.com/aavshr/aws-sdk-go@v1.41.3/private/protocol/xml/xmlutil/xml_to_struct.go (about) 1 package xmlutil 2 3 import ( 4 "encoding/xml" 5 "fmt" 6 "io" 7 "sort" 8 ) 9 10 // A XMLNode contains the values to be encoded or decoded. 11 type XMLNode struct { 12 Name xml.Name `json:",omitempty"` 13 Children map[string][]*XMLNode `json:",omitempty"` 14 Text string `json:",omitempty"` 15 Attr []xml.Attr `json:",omitempty"` 16 17 namespaces map[string]string 18 parent *XMLNode 19 } 20 21 // textEncoder is a string type alias that implemnts the TextMarshaler interface. 22 // This alias type is used to ensure that the line feed (\n) (U+000A) is escaped. 23 type textEncoder string 24 25 func (t textEncoder) MarshalText() ([]byte, error) { 26 return []byte(t), nil 27 } 28 29 // NewXMLElement returns a pointer to a new XMLNode initialized to default values. 30 func NewXMLElement(name xml.Name) *XMLNode { 31 return &XMLNode{ 32 Name: name, 33 Children: map[string][]*XMLNode{}, 34 Attr: []xml.Attr{}, 35 } 36 } 37 38 // AddChild adds child to the XMLNode. 39 func (n *XMLNode) AddChild(child *XMLNode) { 40 child.parent = n 41 if _, ok := n.Children[child.Name.Local]; !ok { 42 n.Children[child.Name.Local] = []*XMLNode{} 43 } 44 n.Children[child.Name.Local] = append(n.Children[child.Name.Local], child) 45 } 46 47 // XMLToStruct converts a xml.Decoder stream to XMLNode with nested values. 48 func XMLToStruct(d *xml.Decoder, s *xml.StartElement) (*XMLNode, error) { 49 out := &XMLNode{} 50 for { 51 tok, err := d.Token() 52 if err != nil { 53 if err == io.EOF { 54 break 55 } else { 56 return out, err 57 } 58 } 59 60 if tok == nil { 61 break 62 } 63 64 switch typed := tok.(type) { 65 case xml.CharData: 66 out.Text = string(typed.Copy()) 67 case xml.StartElement: 68 el := typed.Copy() 69 out.Attr = el.Attr 70 if out.Children == nil { 71 out.Children = map[string][]*XMLNode{} 72 } 73 74 name := typed.Name.Local 75 slice := out.Children[name] 76 if slice == nil { 77 slice = []*XMLNode{} 78 } 79 node, e := XMLToStruct(d, &el) 80 out.findNamespaces() 81 if e != nil { 82 return out, e 83 } 84 node.Name = typed.Name 85 node.findNamespaces() 86 tempOut := *out 87 // Save into a temp variable, simply because out gets squashed during 88 // loop iterations 89 node.parent = &tempOut 90 slice = append(slice, node) 91 out.Children[name] = slice 92 case xml.EndElement: 93 if s != nil && s.Name.Local == typed.Name.Local { // matching end token 94 return out, nil 95 } 96 out = &XMLNode{} 97 } 98 } 99 return out, nil 100 } 101 102 func (n *XMLNode) findNamespaces() { 103 ns := map[string]string{} 104 for _, a := range n.Attr { 105 if a.Name.Space == "xmlns" { 106 ns[a.Value] = a.Name.Local 107 } 108 } 109 110 n.namespaces = ns 111 } 112 113 func (n *XMLNode) findElem(name string) (string, bool) { 114 for node := n; node != nil; node = node.parent { 115 for _, a := range node.Attr { 116 namespace := a.Name.Space 117 if v, ok := node.namespaces[namespace]; ok { 118 namespace = v 119 } 120 if name == fmt.Sprintf("%s:%s", namespace, a.Name.Local) { 121 return a.Value, true 122 } 123 } 124 } 125 return "", false 126 } 127 128 // StructToXML writes an XMLNode to a xml.Encoder as tokens. 129 func StructToXML(e *xml.Encoder, node *XMLNode, sorted bool) error { 130 // Sort Attributes 131 attrs := node.Attr 132 if sorted { 133 sortedAttrs := make([]xml.Attr, len(attrs)) 134 for _, k := range node.Attr { 135 sortedAttrs = append(sortedAttrs, k) 136 } 137 sort.Sort(xmlAttrSlice(sortedAttrs)) 138 attrs = sortedAttrs 139 } 140 141 startElement := xml.StartElement{Name: node.Name, Attr: attrs} 142 143 if node.Text != "" { 144 e.EncodeElement(textEncoder(node.Text), startElement) 145 return e.Flush() 146 } 147 148 e.EncodeToken(startElement) 149 150 if sorted { 151 sortedNames := []string{} 152 for k := range node.Children { 153 sortedNames = append(sortedNames, k) 154 } 155 sort.Strings(sortedNames) 156 157 for _, k := range sortedNames { 158 for _, v := range node.Children[k] { 159 StructToXML(e, v, sorted) 160 } 161 } 162 } else { 163 for _, c := range node.Children { 164 for _, v := range c { 165 StructToXML(e, v, sorted) 166 } 167 } 168 } 169 170 e.EncodeToken(startElement.End()) 171 172 return e.Flush() 173 }