xpp

package module
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2026 License: MIT Imports: 6 Imported by: 0

README

goxpp

Build Status codecov License Go Reference

A lightweight XML pull parser for Go, inspired by Java's XMLPullParser. It provides fine-grained control over XML parsing with a small, cursor-style API.

Features

  • Pull-based parsing for fine-grained document control
  • Scoped namespace and xml:base tracking
  • Efficient navigation and element skipping
  • Errors you can match with errors.As / errors.Is

Installation

go get github.com/mmcdole/goxpp/v2

v1 remains available at github.com/mmcdole/goxpp and receives critical fixes on the v1 branch.

Quick Start

import (
    "encoding/xml"

    xpp "github.com/mmcdole/goxpp/v2"
)

// Parse an RSS feed
file, _ := os.Open("feed.rss")
d := xml.NewDecoder(file)
d.Strict = false
p := xpp.New(d)

// Find the channel element
for tok, err := p.NextTag(); tok != xpp.EndDocument; tok, err = p.NextTag() {
    if err != nil {
        return err
    }
    if tok == xpp.StartTag && p.Name() == "channel" {
        // Process channel contents
        for tok, err = p.NextTag(); tok != xpp.EndTag; tok, err = p.NextTag() {
            if err != nil {
                return err
            }
            if tok == xpp.StartTag {
                switch p.Name() {
                case "title":
                    title, _ := p.NextText()
                    fmt.Printf("Feed: %s\n", title)
                case "item":
                    // Get the item title and skip the rest
                    p.NextTag()
                    title, _ := p.NextText()
                    fmt.Printf("Item: %s\n", title)
                    p.Skip()
                default:
                    p.Skip()
                }
            }
        }
        break
    }
}

Token Types

  • StartDocument, EndDocument
  • StartTag, EndTag
  • Text, Comment
  • ProcessingInstruction, Directive

Migrating from v1

The v2 changes are listed in #34. In short:

  • Construct with xpp.New(*xml.Decoder); configure strictness and charset conversion on the decoder.
  • Cursor state moved from exported fields to methods: p.Name becomes p.Name(), and so on.
  • Namespaces() maps prefix to URI; PrefixForURI covers the reverse lookup.
  • BaseURL() exposes the in-scope xml:base; resolving URLs against it is the caller's concern.
  • Advancement calls after EndDocument return io.EOF; positional failures are *xpp.ExpectError.

Documentation

For detailed documentation and examples, visit pkg.go.dev.

License

This project is licensed under the MIT License.

Documentation

Overview

Package xpp implements a cursor-style XML pull parser over encoding/xml, modeled on Java's XmlPullParser.

The parser is a cursor: an advancement call (Next, NextToken, NextTag) positions it on a token, and accessor methods (Event, Name, Space, Text, Attrs, Depth) describe the token the parser is currently on. Convenience methods (Expect, NextText, Skip, DecodeElement) operate on the current token.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type EventType

type EventType int

EventType identifies the kind of token the parser is positioned on.

const (
	// StartDocument is the parser's state before the first advancement
	// call. It is never returned by Next or NextToken.
	StartDocument EventType = iota
	// EndDocument is returned once when the document ends; every
	// advancement call after that returns io.EOF.
	EndDocument
	StartTag
	EndTag
	Text
	Comment
	ProcessingInstruction
	Directive
)

func (EventType) String

func (e EventType) String() string

type ExpectError

type ExpectError struct {
	WantEvent           EventType
	WantSpace, WantName string
	GotEvent            EventType
	GotSpace, GotName   string
	Offset              int64
}

ExpectError reports a positional assertion failure: the parser was not on the event or name the caller required. It is returned by Expect and ExpectAll, and by the preconditions of NextTag, NextText, Skip and DecodeElement. Want fields hold "*" where anything was acceptable.

func (*ExpectError) Error

func (e *ExpectError) Error() string

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser is a cursor-style XML pull parser. Create one with New; the zero value returns an error from every advancement call.

func New

func New(d *xml.Decoder) *Parser

New returns a parser reading from d. Configure strictness and charset conversion on the decoder directly (d.Strict, d.CharsetReader); the parser adds no configuration of its own.

func (*Parser) Attribute

func (p *Parser) Attribute(name string) string

Attribute returns the value of the named attribute on the current StartTag, or "" if absent. Matching is exact and prefers an un-namespaced attribute; a namespaced attribute is returned only when no plain one shares the local name.

func (*Parser) Attrs

func (p *Parser) Attrs() []xml.Attr

Attrs returns the attributes of the current StartTag. The slice is the parser's live per-token slice: it is valid until the next advancement call, and callers may modify attribute values in place (later reads through Attribute see the modification).

func (*Parser) BaseURL

func (p *Parser) BaseURL() *url.URL

BaseURL returns the xml:base in scope for the current element, or nil when none is declared. Nested xml:base values resolve against their parent per RFC 3986. An unparseable xml:base is treated as absent (the element inherits its parent's base). Resolving document URLs against the base is the caller's concern.

func (*Parser) DecodeElement

func (p *Parser) DecodeElement(v any) error

DecodeElement requires the parser to be on a StartTag and unmarshals the element into v using encoding/xml. On success the cursor is left on the element's end tag. On failure the decoder has stopped at an unknown position inside the element, so the parser is poisoned: DecodeElement returns the decoder's error and every later call returns the wrapped form.

func (*Parser) Depth

func (p *Parser) Depth() int

Depth returns the element nesting depth of the current token. An EndTag reports the same depth as its matching StartTag; the root element is depth 1.

func (*Parser) Err

func (p *Parser) Err() error

Err returns the sticky error, or nil while the parser is healthy. It is set by a decoder error or a failed DecodeElement, after which every advancement call returns it.

func (*Parser) Event

func (p *Parser) Event() EventType

Event returns the type of the token the parser is on. Before the first advancement call it is StartDocument.

func (*Parser) Expect

func (p *Parser) Expect(event EventType, name string) error

Expect returns nil when the parser is on the given event with the given local name. Name matching is case-insensitive, a documented leniency for real-world feed input; "*" matches any name.

func (*Parser) ExpectAll

func (p *Parser) ExpectAll(event EventType, space, name string) error

ExpectAll is Expect with an additional namespace assertion, matched the same way.

func (*Parser) InputOffset

func (p *Parser) InputOffset() int64

InputOffset returns the input stream byte offset of the current decoder position.

func (*Parser) IsWhitespace

func (p *Parser) IsWhitespace() bool

IsWhitespace reports whether the current Text token is entirely whitespace.

func (*Parser) Name

func (p *Parser) Name() string

Name returns the local name of the current start or end tag.

func (*Parser) Namespaces

func (p *Parser) Namespaces() map[string]string

Namespaces returns the prefix -> URI bindings in scope for the current element, with prefix case preserved. The default namespace is bound to the empty prefix. The map is a snapshot, safe to retain and modify.

func (*Parser) Next

func (p *Parser) Next() (EventType, error)

Next advances like NextToken but skips Comment, ProcessingInstruction and Directive tokens.

func (*Parser) NextTag

func (p *Parser) NextTag() (EventType, error)

NextTag advances past any whitespace text and returns the next StartTag or EndTag. Anything else is an error.

func (*Parser) NextText

func (p *Parser) NextText() (string, error)

NextText requires the parser to be on a StartTag, consumes the element's text content, and leaves the parser on the matching EndTag.

func (*Parser) NextToken

func (p *Parser) NextToken() (EventType, error)

NextToken advances to the next raw token, including comments, processing instructions and directives. The first call after the document ends returns (EndDocument, nil); every call after that returns io.EOF. After a decoder error or a failed DecodeElement the parser is poisoned and every call returns that error; see Err.

func (*Parser) PrefixForURI

func (p *Parser) PrefixForURI(uri string) (prefix string, ok bool)

PrefixForURI returns the most recently declared in-scope prefix bound to uri. It reports ok=false when no in-scope prefix is bound to it.

func (*Parser) Skip

func (p *Parser) Skip() error

Skip requires the parser to be on a StartTag and consumes tokens through the matching end tag. It is iterative (a depth counter rather than recursion) so deeply nested input can't overflow the goroutine stack.

func (*Parser) Space

func (p *Parser) Space() string

Space returns the namespace URI of the current start or end tag.

func (*Parser) Text

func (p *Parser) Text() string

Text returns the content of the current Text, Comment or Directive token.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL