package util import ( "bufio" ) type XMLParser struct { reader *bufio.Reader scratch *scratch selfClose bool lastElement string } func NewXMLParser(r *bufio.Reader) *XMLParser { x := &XMLParser{ reader: r, scratch: &scratch{data: make([]byte, 1024)}, } x.skipDeclerations() return x } func (x *XMLParser) skipDeclerations() error { for { b, err := x.reader.ReadByte() if err != nil { return err } if b == '>' { return nil } } } type TokenType int const ( StartElement TokenType = iota EndElement ) func (x *XMLParser) Token() (TokenType, string, error) { if x.selfClose { x.selfClose = false return EndElement, x.lastElement, nil } var ( f, c bool ) for { b, err := x.reader.ReadByte() if err != nil { return 0, "", err } if b == '<' { f = true x.scratch.reset() b, err := x.reader.ReadByte() if err != nil { return 0, "", err } if b == '/' { c = true } else { x.scratch.add(b) } } else if b == '>' { bs := x.scratch.bytes() if bs[len(bs)-1] == '/' { x.selfClose = true x.lastElement = string(bs[:len(bs)-1]) return StartElement, x.lastElement, nil } else { if c { return EndElement, string(bs), nil } else { return StartElement, string(bs), nil } } } else if f { x.scratch.add(b) } } } func (x *XMLParser) Value() ([]byte, error) { if x.selfClose { x.selfClose = false return nil, nil } x.scratch.reset() var ( f bool ) for { b, err := x.reader.ReadByte() if err != nil { return nil, err } if b == '<' { f = true } else if f && b == '>' { return x.scratch.bytes(), nil } else if !f { x.scratch.add(b) } } } func (x *XMLParser) Skip() error { depth := 0 for { t, _, err := x.Token() if err != nil { return err } switch t { case StartElement: depth++ case EndElement: if depth == 0 { return nil } depth-- } } } // scratch taken from //https://github.com/bcicen/jstream type scratch struct { data []byte fill int } // reset scratch buffer func (s *scratch) reset() { s.fill = 0 } // bytes returns the written contents of scratch buffer func (s *scratch) bytes() []byte { return s.data[0:s.fill] } // grow scratch buffer func (s *scratch) grow() { ndata := make([]byte, cap(s.data)*2) copy(ndata, s.data[:]) s.data = ndata } // append single byte to scratch buffer func (s *scratch) add(c byte) { if s.fill+1 >= cap(s.data) { s.grow() } s.data[s.fill] = c s.fill++ }