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  }