-
Notifications
You must be signed in to change notification settings - Fork 50
/
parser.go
106 lines (82 loc) · 1.88 KB
/
parser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package xmlparsing
import (
"encoding/xml"
)
func Parse(rawXML string) (map[string]interface{}, error) {
var n node
if err := xml.Unmarshal([]byte(rawXML), &n); err != nil {
return nil, err
}
return buildMap([]node{n}), nil
}
type node struct {
XMLName xml.Name
Attrs []xml.Attr `xml:",any,attr"`
Content string `xml:",chardata"`
Children []node `xml:",any"`
}
func buildMap(nodes []node) map[string]interface{} {
result := make(map[string]interface{})
for name, group := range regroupNodesByName(nodes) {
if len(group) == 0 {
continue
}
if len(group) == 1 {
result[name] = buildNode(group[0])
} else {
result[name] = buildArray(group)
}
}
return result
}
func buildArray(nodes []node) []interface{} {
arr := make([]interface{}, len(nodes))
for i, n := range nodes {
arr[i] = buildNode(n)
}
return arr
}
func buildNode(n node) interface{} {
hasAttrs := len(n.Attrs) > 0
hasChildren := len(n.Children) > 0
if hasAttrs && hasChildren {
result := buildMap(n.Children)
result["-attrs"] = buildAttributes(n.Attrs)
return result
}
if hasAttrs {
return map[string]interface{}{
"-attrs": buildAttributes(n.Attrs),
"content": n.Content,
}
}
if hasChildren {
return buildMap(n.Children)
}
return n.Content
}
func buildAttributes(attrs []xml.Attr) map[string]string {
m := make(map[string]string, len(attrs))
for _, attr := range attrs {
m[joinXMLName(attr.Name)] = attr.Value
}
return m
}
func regroupNodesByName(nodes []node) map[string][]node {
grouped := make(map[string][]node)
for _, n := range nodes {
name := joinXMLName(n.XMLName)
if _, ok := grouped[name]; !ok {
grouped[name] = make([]node, 0)
}
grouped[name] = append(grouped[name], n)
}
return grouped
}
func joinXMLName(xmlName xml.Name) string {
name := xmlName.Local
if xmlName.Space != "" {
name = xmlName.Space + ":" + name
}
return name
}