-
Notifications
You must be signed in to change notification settings - Fork 3
/
xml_scan.go
134 lines (117 loc) · 3.04 KB
/
xml_scan.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package llsd
import (
"encoding/xml"
"fmt"
"io"
"reflect"
)
type XMLScanner struct {
dec *xml.Decoder
}
func NewXMLScanner(r io.Reader) *XMLScanner {
return &XMLScanner{dec: xml.NewDecoder(r)}
}
// InputOffset returns the input stream byte offset of the current decoder position.
func (s *XMLScanner) Offset() int64 {
return s.dec.InputOffset()
}
func (s *XMLScanner) charData() ([]byte, error) {
// Attempt to get CharData (inner-text)
t, err := s.dec.Token()
if err != nil {
return nil, err
}
switch ty := t.(type) {
case xml.CharData:
return ty, nil
case xml.EndElement:
// Handle self-closing elements
return nil, nil
default:
return nil, fmt.Errorf("Invalid LLSD: got unexpected %s", reflect.TypeOf(t))
}
}
// Skip element, useful for jumping over large maps and arrays.
func (s *XMLScanner) Skip() error {
return s.dec.Skip()
}
func (s *XMLScanner) Token() (Token, error) {
tok, err := s.dec.Token()
if err != nil {
return nil, err
}
switch ty := tok.(type) {
case xml.StartElement:
switch ty.Name.Local {
case "array":
return ArrayStart{}, nil
case "map":
return MapStart{}, nil
case "key":
b, err := s.charData()
key := Key(b)
if err != nil {
return key, err
}
// Advanced past </key> EndElement, which is always provided by go's xml decoding
_, err = s.dec.Token()
return key, err
case "llsd":
// Skip document start
return s.Token()
default:
scalarTypes := map[string]ScalarType{
"string": String,
"real": Real,
"uuid": UUIDType,
"integer": Integer,
"boolean": Boolean,
"undef": Undefined,
"binary": Binary,
"uri": URI,
"date": Date,
}
scalarType, ok := scalarTypes[ty.Name.Local]
if !ok {
return nil, fmt.Errorf("Unknown LLSD type \"%s\"", ty.Name.Local)
}
// Copy data so that it is not overwritten when advancing past end element
innerText, err := s.charData()
data := make([]byte, len(innerText))
copy(data, innerText)
// Map XML attributes (<binary encoding="base64">)
attr := map[string]string{}
for _, a := range ty.Attr {
attr[a.Name.Local] = a.Value
}
if err != nil {
return nil, err
}
// If innerText is nil then the element is self-closing and we have
// already advanced past its EndElement.
if innerText != nil {
// Advanced past EndElement, which is always provided by go's xml decoding
_, err = s.dec.Token()
}
return Scalar{Type: scalarType, Data: data, Attr: attr}, err
}
case xml.EndElement:
switch ty.Name.Local {
case "array":
return ArrayEnd{}, nil
case "map":
return MapEnd{}, nil
case "llsd":
return s.Token()
default:
return nil, fmt.Errorf("Invalid LLSD: unexpected EndElement %s", ty.Name.Local)
}
case xml.Comment, xml.ProcInst, xml.CharData:
// Skip comments, (<!-- ... -->)
// Skip XML processing instructions, (<xml ... >)
// Skip character data between elements such as whitespace
return s.Token()
default:
return nil, fmt.Errorf("Invalid LLSD. Unexpected %s at %d", reflect.TypeOf(tok), s.Offset())
}
}