-
Notifications
You must be signed in to change notification settings - Fork 0
/
reader.go
113 lines (100 loc) · 1.94 KB
/
reader.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
package xml
import (
"bufio"
"io"
)
// Reader represents a XML reader.
type Reader struct {
r *bufio.Reader
err error
e Element
n *string
}
// NewReader returns a initialized reader.
func NewReader(r io.Reader) *Reader {
return &Reader{
r: bufio.NewReaderSize(r, 2<<12),
}
}
// Element returns the last readed element.
func (r *Reader) Element() Element {
return r.e
}
// Error return the last error.
func (r *Reader) Error() error {
return r.err
}
func (r *Reader) release() {
if r.e == nil {
return
}
if e, ok := r.e.(*StartElement); ok {
releaseStart(e)
} else if e, ok := r.e.(*EndElement); ok {
releaseEnd(e)
}
r.e = nil
}
// Next iterates until the next XML element.
func (r *Reader) Next() bool {
r.release()
var c byte
for r.e == nil && r.err == nil {
c, r.err = skipWS(r.r)
if r.err == nil {
switch c { // get next token
case '<': // new element
r.next()
default: // text string
r.r.UnreadByte()
t, err := r.r.ReadString('<') // read until a new element starts (or EOF is reached)
if err != nil {
r.err = err
} else {
t = t[:len(t)-1]
if r.n != nil {
*r.n, r.n = t, nil
} else {
tt := TextElement(t)
r.e = &tt
}
r.r.UnreadByte()
}
}
}
}
return r.e != nil && r.err == nil
}
// AssignNext will assign the next TextElement to ptr.
func (r *Reader) AssignNext(ptr *string) {
r.n = ptr
}
// skip reads until the next end tag '>'
func (r *Reader) skip() error {
_, err := r.r.ReadBytes('>')
return err
}
// next will read the next byte after finding '<'
func (r *Reader) next() {
var c byte
c, r.err = skipWS(r.r)
if r.err == nil {
switch c {
case '/':
r.e = endPool.Get().(*EndElement)
case '!':
r.err = r.skip()
case '?':
r.err = r.skip()
default:
r.e = startPool.Get().(*StartElement)
r.r.UnreadByte()
}
if r.err == nil && r.e != nil {
r.err = r.e.parse(r.r)
if r.err != nil {
r.e = nil
}
}
}
}