]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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 | // NewXMLElement returns a pointer to a new XMLNode initialized to default values. | |
22 | func NewXMLElement(name xml.Name) *XMLNode { | |
23 | return &XMLNode{ | |
24 | Name: name, | |
25 | Children: map[string][]*XMLNode{}, | |
26 | Attr: []xml.Attr{}, | |
27 | } | |
28 | } | |
29 | ||
30 | // AddChild adds child to the XMLNode. | |
31 | func (n *XMLNode) AddChild(child *XMLNode) { | |
15c0b25d | 32 | child.parent = n |
bae9f6d2 JC |
33 | if _, ok := n.Children[child.Name.Local]; !ok { |
34 | n.Children[child.Name.Local] = []*XMLNode{} | |
35 | } | |
36 | n.Children[child.Name.Local] = append(n.Children[child.Name.Local], child) | |
37 | } | |
38 | ||
39 | // XMLToStruct converts a xml.Decoder stream to XMLNode with nested values. | |
40 | func XMLToStruct(d *xml.Decoder, s *xml.StartElement) (*XMLNode, error) { | |
41 | out := &XMLNode{} | |
42 | for { | |
43 | tok, err := d.Token() | |
44 | if err != nil { | |
45 | if err == io.EOF { | |
46 | break | |
47 | } else { | |
48 | return out, err | |
49 | } | |
50 | } | |
51 | ||
52 | if tok == nil { | |
53 | break | |
54 | } | |
55 | ||
56 | switch typed := tok.(type) { | |
57 | case xml.CharData: | |
58 | out.Text = string(typed.Copy()) | |
59 | case xml.StartElement: | |
60 | el := typed.Copy() | |
61 | out.Attr = el.Attr | |
62 | if out.Children == nil { | |
63 | out.Children = map[string][]*XMLNode{} | |
64 | } | |
65 | ||
66 | name := typed.Name.Local | |
67 | slice := out.Children[name] | |
68 | if slice == nil { | |
69 | slice = []*XMLNode{} | |
70 | } | |
71 | node, e := XMLToStruct(d, &el) | |
72 | out.findNamespaces() | |
73 | if e != nil { | |
74 | return out, e | |
75 | } | |
76 | node.Name = typed.Name | |
77 | node.findNamespaces() | |
78 | tempOut := *out | |
79 | // Save into a temp variable, simply because out gets squashed during | |
80 | // loop iterations | |
81 | node.parent = &tempOut | |
82 | slice = append(slice, node) | |
83 | out.Children[name] = slice | |
84 | case xml.EndElement: | |
85 | if s != nil && s.Name.Local == typed.Name.Local { // matching end token | |
86 | return out, nil | |
87 | } | |
88 | out = &XMLNode{} | |
89 | } | |
90 | } | |
91 | return out, nil | |
92 | } | |
93 | ||
94 | func (n *XMLNode) findNamespaces() { | |
95 | ns := map[string]string{} | |
96 | for _, a := range n.Attr { | |
97 | if a.Name.Space == "xmlns" { | |
98 | ns[a.Value] = a.Name.Local | |
99 | } | |
100 | } | |
101 | ||
102 | n.namespaces = ns | |
103 | } | |
104 | ||
105 | func (n *XMLNode) findElem(name string) (string, bool) { | |
106 | for node := n; node != nil; node = node.parent { | |
107 | for _, a := range node.Attr { | |
108 | namespace := a.Name.Space | |
109 | if v, ok := node.namespaces[namespace]; ok { | |
110 | namespace = v | |
111 | } | |
112 | if name == fmt.Sprintf("%s:%s", namespace, a.Name.Local) { | |
113 | return a.Value, true | |
114 | } | |
115 | } | |
116 | } | |
117 | return "", false | |
118 | } | |
119 | ||
120 | // StructToXML writes an XMLNode to a xml.Encoder as tokens. | |
121 | func StructToXML(e *xml.Encoder, node *XMLNode, sorted bool) error { | |
122 | e.EncodeToken(xml.StartElement{Name: node.Name, Attr: node.Attr}) | |
123 | ||
124 | if node.Text != "" { | |
125 | e.EncodeToken(xml.CharData([]byte(node.Text))) | |
126 | } else if sorted { | |
127 | sortedNames := []string{} | |
128 | for k := range node.Children { | |
129 | sortedNames = append(sortedNames, k) | |
130 | } | |
131 | sort.Strings(sortedNames) | |
132 | ||
133 | for _, k := range sortedNames { | |
134 | for _, v := range node.Children[k] { | |
135 | StructToXML(e, v, sorted) | |
136 | } | |
137 | } | |
138 | } else { | |
139 | for _, c := range node.Children { | |
140 | for _, v := range c { | |
141 | StructToXML(e, v, sorted) | |
142 | } | |
143 | } | |
144 | } | |
145 | ||
146 | e.EncodeToken(xml.EndElement{Name: node.Name}) | |
147 | return e.Flush() | |
148 | } |