Documentation
¶
Overview ¶
Package xmlstream provides an API for streaming, transforming, and otherwise manipulating XML data.
This package only builds against versions of Go that include this patch: https://golang.org/cl/38791
BE ADVISED: The API is unstable and subject to change.
Index ¶
- Variables
- func Encode(e TokenWriter, d xml.TokenReader) (err error)
- func Fmt(d xml.TokenReader, opts ...FmtOption) xml.TokenReader
- func InnerReader(r io.Reader) io.Reader
- func MultiReader(readers ...xml.TokenReader) xml.TokenReader
- func Pipe() (*PipeReader, *PipeWriter)
- func Unwrap(r xml.TokenReader) xml.TokenReader
- func Wrap(start xml.StartElement, r xml.TokenReader) xml.TokenReader
- type FmtOption
- type PipeReader
- type PipeWriter
- type ReaderFunc
- type TokenWriter
- type Transformer
- func Inspect(f func(t xml.Token)) Transformer
- func Map(mapping func(t xml.Token) xml.Token) Transformer
- func Remove(f func(t xml.Token) bool) Transformer
- func RemoveAttr(f func(start xml.StartElement, attr xml.Attr) bool) Transformer
- func RemoveElement(f func(start xml.StartElement) bool) Transformer
- Bugs
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrClosedPipe = errors.New("xmlstream: read/write on closed pipe")
ErrClosedPipe is the error used for read or write operations on a closed pipe.
Functions ¶
func Encode ¶
func Encode(e TokenWriter, d xml.TokenReader) (err error)
Encode consumes an xml.TokenReader and encodes any tokens that it outputs. If an error is returned on the Decode or Encode side, it is returned immediately. Since Encode is defined as consuming the stream until the end, io.EOF is not returned. If no error would be returned, Encode flushes the TokenWriter when it is done.
Example ¶
package main
import (
"encoding/xml"
"log"
"os"
"strings"
"mellium.im/xmlstream"
)
func main() {
removequote := xmlstream.Remove(func(t xml.Token) bool {
switch tok := t.(type) {
case xml.StartElement:
return tok.Name.Local == "quote"
case xml.EndElement:
return tok.Name.Local == "quote"
}
return false
})
e := xml.NewEncoder(os.Stdout)
err := xmlstream.Encode(e, removequote(xml.NewDecoder(strings.NewReader(`
<quote>
<p>Foolery, sir, does walk about the orb, like the sun; it shines everywhere.</p>
</quote>`))))
if err != nil {
log.Fatal("Error in Encode example:", err)
}
}
Output: <p>Foolery, sir, does walk about the orb, like the sun; it shines everywhere.</p>
func Fmt ¶
func Fmt(d xml.TokenReader, opts ...FmtOption) xml.TokenReader
Fmt returns a transformer that indents the given XML stream. The default indentation style is to remove non-significant whitespace, start elements on a new line and indent two spaces per level.
Example (Indentation) ¶
package main
import (
"bytes"
"encoding/xml"
"fmt"
"strings"
"mellium.im/xmlstream"
)
func main() {
tokenizer := xmlstream.Fmt(xml.NewDecoder(strings.NewReader(`
<quote> <p>
<!-- Chardata is not indented -->
How now, my hearts! did you never see the picture
of 'we three'?</p>
</quote>`)), xmlstream.Prefix("\n"), xmlstream.Indent(" "))
buf := new(bytes.Buffer)
e := xml.NewEncoder(buf)
for t, err := tokenizer.Token(); err == nil; t, err = tokenizer.Token() {
e.EncodeToken(t)
}
e.Flush()
fmt.Println(buf.String())
}
Output: <quote> <p> <!-- Chardata is not indented --> How now, my hearts! did you never see the picture of 'we three'? </p> </quote>
func InnerReader ¶
InnerReader is an io.Reader which attempts to decode an xml.StartElement from the stream on the first call to Read (returning an error if an invalid start token is found) and returns a new reader which only reads the inner XML without parsing it or checking its validity. After the inner XML is read, the end token is parsed and if it does not exist or does not match the original start token an error is returned.
Example ¶
package main
import (
"io"
"os"
"strings"
"mellium.im/xmlstream"
)
func main() {
r := xmlstream.InnerReader(strings.NewReader(`<stream:features>
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>
<required/>
</starttls>
</stream:features>`))
io.Copy(os.Stdout, r)
}
Output: <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'> <required/> </starttls>
func MultiReader ¶ added in v0.1.0
func MultiReader(readers ...xml.TokenReader) xml.TokenReader
MultiReader returns an xml.TokenReader that's the logical concatenation of the provided input readers. They're read sequentially. Once all inputs have returned io.EOF, Token will return io.EOF. If any of the readers return a non-nil, non-EOF error, Token will return that error.
Example ¶
package main
import (
"encoding/xml"
"log"
"os"
"strings"
"mellium.im/xmlstream"
)
func main() {
e := xml.NewEncoder(os.Stdout)
e.Indent("", " ")
r1 := xml.NewDecoder(strings.NewReader(`<title>Dover Beach</title>`))
r2 := xml.NewDecoder(strings.NewReader(`<author>Matthew Arnold</author>`))
r3 := xml.NewDecoder(strings.NewReader(`<incipit>The sea is calm to-night.</incipit>`))
r := xmlstream.MultiReader(r1, r2, r3)
if err := xmlstream.Encode(e, r); err != nil {
log.Fatal("Error in MultiReader example:", err)
}
}
Output: <title>Dover Beach</title> <author>Matthew Arnold</author> <incipit>The sea is calm to-night.</incipit>
func Pipe ¶ added in v0.1.0
func Pipe() (*PipeReader, *PipeWriter)
Pipe creates a synchronous in-memory pipe of tokens. It can be used to connect code expecting an xml.TokenReader with code expecting an xmlstream.TokenWriter.
Reads and Writes on the pipe are matched one to one. That is, each Write to the PipeWriter blocks until it has satisfied a Read from the corresponding PipeReader.
It is safe to call Read and Write in parallel with each other or with Close. Parallel calls to Read and parallel calls to Write are also safe: the individual calls will be gated sequentially.
func Unwrap ¶ added in v0.1.0
func Unwrap(r xml.TokenReader) xml.TokenReader
Unwrap returns a new token reader that skips the first token read from it and, if it is a start element, also skips its corresponding end element. If the element is not a start element it is returned along with an error.
Example ¶
package main
import (
"encoding/xml"
"log"
"os"
"strings"
"mellium.im/xmlstream"
)
func main() {
var r xml.TokenReader = xml.NewDecoder(strings.NewReader(`<message from="ismene@example.org/dIoK6Wi3"><body>No mind that ever lived stands firm in evil days, but goes astray.</body></message>`))
e := xml.NewEncoder(os.Stdout)
r = xmlstream.Unwrap(r)
if err := xmlstream.Encode(e, r); err != nil {
log.Fatal("Error in unwrap example:", err)
}
}
Output: <body>No mind that ever lived stands firm in evil days, but goes astray.</body>
func Wrap ¶ added in v0.1.0
func Wrap(start xml.StartElement, r xml.TokenReader) xml.TokenReader
Wrap wraps a token stream in a start element and its corresponding end element.
Example ¶
package main
import (
"encoding/xml"
"log"
"os"
"strings"
"mellium.im/xmlstream"
)
func main() {
var r xml.TokenReader = xml.NewDecoder(strings.NewReader(`<body>No mind that ever lived stands firm in evil days, but goes astray.</body>`))
e := xml.NewEncoder(os.Stdout)
e.Indent("", " ")
r = xmlstream.Wrap(xml.StartElement{
Name: xml.Name{Local: "message"},
Attr: []xml.Attr{
{Name: xml.Name{Local: "from"}, Value: "ismene@example.org/Fo6Eeb2e"},
},
}, r)
if err := xmlstream.Encode(e, r); err != nil {
log.Fatal("Error in wrap example:", err)
}
}
Output: <message from="ismene@example.org/Fo6Eeb2e"> <body>No mind that ever lived stands firm in evil days, but goes astray.</body> </message>
Types ¶
type FmtOption ¶
type FmtOption func(*fmter)
FmtOption is used to configure a formatters behavior.
type PipeReader ¶ added in v0.1.0
type PipeReader struct {
// contains filtered or unexported fields
}
A PipeReader is the read half of a token pipe.
func (*PipeReader) Close ¶ added in v0.1.0
func (r *PipeReader) Close() error
Close closes the PipeReader; subsequent reads from the read half of the pipe will return no bytes and EOF.
func (*PipeReader) CloseWithError ¶ added in v0.1.0
func (r *PipeReader) CloseWithError(err error)
CloseWithError closes the PipeReader; subsequent reads from the read half of the pipe will return no tokens and the error err, or EOF if err is nil.
func (*PipeReader) Token ¶ added in v0.1.0
func (r *PipeReader) Token() (t xml.Token, err error)
Token implements the xml.TokenReader interface. It reads a token from the pipe, blocking until a writer arrives or the write end is closed. If the write end is closed with an error, that error is returned as err; otherwise err is io.EOF.
type PipeWriter ¶ added in v0.1.0
type PipeWriter struct {
// contains filtered or unexported fields
}
A PipeWriter is the write half of a token pipe.
func (*PipeWriter) Close ¶ added in v0.1.0
func (w *PipeWriter) Close() error
Close closes the PipeWriter; subsequent reads from the read half of the pipe will return no bytes and EOF.
func (*PipeWriter) CloseWithError ¶ added in v0.1.0
func (w *PipeWriter) CloseWithError(err error)
CloseWithError closes the PipeWriter; subsequent reads from the read half of the pipe will return no tokens and the error err, or EOF if err is nil.
func (*PipeWriter) EncodeToken ¶ added in v0.1.0
func (w *PipeWriter) EncodeToken(t xml.Token) error
EncodeToken implements the TokenWriter interface. It writes a token to the pipe, blocking until one or more readers have consumed all the data or the read end is closed. If the read end is closed with an error, that err is returned as err; otherwise err is ErrClosedPipe.
func (*PipeWriter) Flush ¶ added in v0.1.0
func (w *PipeWriter) Flush() error
Flush is currently a noop and always returns nil.
type ReaderFunc ¶ added in v0.1.0
ReaderFunc type is an adapter to allow the use of ordinary functions as an xml.TokenReader. If f is a function with the appropriate signature, ReaderFunc(f) is an xml.TokenReader that calls f.
Example ¶
package main
import (
"encoding/xml"
"io"
"log"
"os"
"mellium.im/xmlstream"
)
func main() {
state := 0
start := xml.StartElement{Name: xml.Name{Local: "quote"}}
d := xmlstream.ReaderFunc(func() (xml.Token, error) {
switch state {
case 0:
state++
return start, nil
case 1:
state++
return xml.CharData("the rain it raineth every day"), nil
case 2:
state++
return start.End(), nil
default:
return nil, io.EOF
}
})
e := xml.NewEncoder(os.Stdout)
if err := xmlstream.Encode(e, d); err != nil {
log.Fatal("Error in func example:", err)
}
}
Output: <quote>the rain it raineth every day</quote>
type TokenWriter ¶
TokenWriter is anything that can encode tokens to an XML stream, including an xml.Encoder.
type Transformer ¶
type Transformer func(src xml.TokenReader) xml.TokenReader
A Transformer returns a new xml.TokenReader that returns transformed tokens read from src.
func Inspect ¶
func Inspect(f func(t xml.Token)) Transformer
Inspect performs an operation for each token in the stream without transforming the stream in any way.
func Map ¶
func Map(mapping func(t xml.Token) xml.Token) Transformer
Map returns a Transformer that maps the tokens in the input using the given mapping.
func Remove ¶
func Remove(f func(t xml.Token) bool) Transformer
Remove returns a Transformer that removes tokens for which f matches.
Example ¶
package main
import (
"bytes"
"encoding/xml"
"fmt"
"strings"
"mellium.im/xmlstream"
)
func main() {
removequote := xmlstream.Remove(func(t xml.Token) bool {
switch tok := t.(type) {
case xml.StartElement:
return tok.Name.Local == "quote"
case xml.EndElement:
return tok.Name.Local == "quote"
}
return false
})
tokenizer := removequote(xml.NewDecoder(strings.NewReader(`
<quote>
<p>Foolery, sir, does walk about the orb, like the sun; it shines everywhere.</p>
</quote>`)))
buf := new(bytes.Buffer)
e := xml.NewEncoder(buf)
for t, err := tokenizer.Token(); err == nil; t, err = tokenizer.Token() {
e.EncodeToken(t)
}
e.Flush()
fmt.Println(buf.String())
}
Output: <p>Foolery, sir, does walk about the orb, like the sun; it shines everywhere.</p>
func RemoveAttr ¶
func RemoveAttr(f func(start xml.StartElement, attr xml.Attr) bool) Transformer
RemoveAttr returns a Transformer that removes attributes from xml.StartElement's if f matches.
func RemoveElement ¶
func RemoveElement(f func(start xml.StartElement) bool) Transformer
RemoveElement returns a Transformer that removes entire elements (and their children) if f matches the elements start token.
Example ¶
package main
import (
"bytes"
"encoding/xml"
"fmt"
"strings"
"mellium.im/xmlstream"
)
func main() {
removeLangEn := xmlstream.RemoveElement(func(start xml.StartElement) bool {
// TODO: Probably be more specific and actually check the name.
if len(start.Attr) > 0 && start.Attr[0].Value == "en" {
return true
}
return false
})
d := removeLangEn(xml.NewDecoder(strings.NewReader(`
<quote>
<p xml:lang="en">Thus the whirligig of time brings in his revenges.</p>
<p xml:lang="fr">et c’est ainsi que la roue du temps amène les occasions de revanche.</p>
</quote>
`)))
buf := new(bytes.Buffer)
e := xml.NewEncoder(buf)
for t, err := d.Token(); err == nil; t, err = d.Token() {
e.EncodeToken(t)
}
e.Flush()
fmt.Println(buf.String())
}
Output: <quote> <p xml:lang="fr">et c’est ainsi que la roue du temps amène les occasions de revanche.</p> </quote>
Notes ¶
Bugs ¶
Multiple uses of RemoveAttr will iterate over the attr list
multiple times.