xmlwriter

package module
v0.0.0-...-85336ec Latest Latest
Warning

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

Go to latest
Published: May 25, 2023 License: Apache-2.0 Imports: 8 Imported by: 23

README

xmlwriter

GoDoc Go

xmlwriter is a pure-Go library providing a procedural XML generation API based on libxml2's xmlwriter module. The package is extensively documented at GoDoc.

Quick example:

func main() {
    b := &bytes.Buffer{}
    w := xmlwriter.Open(b)
    ec := &xmlwriter.ErrCollector{}
    defer ec.Panic()

    ec.Do(
        w.StartDoc(xmlwriter.Doc{})
        w.StartElem(xmlwriter.Elem{Name: "foo"})
        w.WriteAttr(xmlwriter.Attr{Name: "a1", Value: "val1"})
        w.WriteAttr(xmlwriter.Attr{Name: "a2", Value: "val2"})
        w.WriteComment(xmlwriter.Comment{"hello"})
        w.StartElem(xmlwriter.Elem{Name: "bar"})
        w.WriteAttr(xmlwriter.Attr{Name: "a1", Value: "val1"})
        w.WriteAttr(xmlwriter.Attr{Name: "a2", Value: "val2"})
        w.StartElem(xmlwriter.Elem{Name: "baz"})
        w.EndAllFlush()
    )
    fmt.Println(b.String())
}

xmlwriter is about twice as quick as using the stdlib's encoding/xml and offers total control of the output. If you don't require that level of control, it's probably better to stick with encoding/xml

BenchmarkWriterHuge-8     	     165	   7189290 ns/op	    4944 B/op	       4 allocs/op
BenchmarkWriterSmall-8    	  299679	      4035 ns/op	    4944 B/op	       4 allocs/op
BenchmarkGolangHuge-8      	      52	  21770422 ns/op	 4324496 B/op	   60008 allocs/op
BenchmarkGolangSmall-8    	  139767	      8828 ns/op	    5936 B/op	      28 allocs/op

xmlwriter is exhaustively tested using a fairly insane mess of C scripts you can find in the tester/ directory.

License

xmlwriter uses the Apache License 2.0. I pulled in about 60 lines of code from the xml/encoding package in the Go sources and retained the copyright. Not sure the exact implications, IANAL. Please file an issue if I've done something wrong.

Documentation

Overview

Package xmlwriter provides a fast, non-cached, forward-only way to generate XML data.

The API is based heavily on libxml's xmlwriter API [1], which is itself based on C#'s XmlWriter [2].

[1] http://xmlsoft.org/html/libxml-xmlwriter.html
[2] https://msdn.microsoft.com/en-us/library/system.xml.xmlwriter(v=vs.110).aspx

It offers some advantages over Go's default encoding/xml package and some tradeoffs. You can have complete control of the generated documents and it uses very little memory.

There are two styles for interacting with the writer: structured and heap-friendly. If you want a visual representation of the hierarchy of some of your writes in your code and you don't care about a few instances of memory escaping to the heap (and most of the time you won't), you can use the structured API. If you are writing a code generator or your interactions with the API are minimal, you should use the direct API.

Creating

xmlwriter.Writer{} takes any io.Writer, along with a variable list of options.

b := &bytes.Buffer{}
w := xmlwriter.Open(b)

xmlwriter options are based on Dave Cheney's functional options pattern (https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis):

w := xmlwriter.NewWriter(b, xmlwriter.WithIndent())

Provided options are:

  • WithIndent()
  • WithIndentString(string)

Overview

Using the structured API, you might express a small tree of elements like this. These nodes will escape to the heap, but judicious use of this nesting can make certain structures a lot more readable by representing the desired XML hierarchy in the code that produces it:

ec := &xmlwriter.ErrCollector{}
defer ec.Panic()
ec.Do(
	w.Start(xmlwriter.Doc{}),
	w.Start(xmlwriter.Elem{
		Name: "foo", Attrs: []xmlwriter.Attr{
			{Name: "a1", Value: "val1"},
			{Name: "a2", Value: "val2"},
		},
		Content: []xmlwriter.Writable{
			xmlwriter.Comment{"hello"},
			xmlwriter.Elem{
				Name: "bar", Attrs: []xmlwriter.Attr{
					{Name: "a1", Value: "val1"},
					{Name: "a2", Value: "val2"},
				},
				Content: []xmlwriter.Writable{
					xmlwriter.Elem{Name: "baz"},
				},
			},
		},
	}),
	w.EndAllFlush(),
)

The code can be made even less dense by importing xmlwriter with a prefix: `import xw "github.com/shabbyrobe/xmlwriter"`

The same output is possible with the heap-friendy API. This has a lot more stutter and it's harder to tell the hierarchical relationship just by looking at the code, but there are no heap escapes this way:

ec := &xmlwriter.ErrCollector{}
defer ec.Panic()

ec.Do(
	w.StartDoc(xmlwriter.Doc{})
	w.StartElem(xmlwriter.Elem{Name: "foo"})
	w.WriteAttr(xmlwriter.Attr{Name: "a1", Value: "val1"})
	w.WriteAttr(xmlwriter.Attr{Name: "a2", Value: "val2"})
	w.WriteComment(xmlwriter.Comment{"hello"})
	w.StartElem(xmlwriter.Elem{Name: "bar"})
	w.WriteAttr(xmlwriter.Attr{Name: "a1", Value: "val1"})
	w.WriteAttr(xmlwriter.Attr{Name: "a2", Value: "val2"})
	w.StartElem(xmlwriter.Elem{Name: "baz"})
	w.EndAllFlush()
)

Use whichever API reads best in your code, but favour the latter style in all code generators and performance hotspots.

Flush

xmlwriter.Writer extends bufio.Writer! Don't forget to flush otherwise you'll lose data.

There are two ways to flush:

  • Writer.Flush()
  • Writer.EndAllFlush()

The EndAllFlush form is just a convenience, it calls EndAll() and Flush() for you.

Start and Write and Block

Nodes which can have children can be passed to `Writer.Start()`. This adds them to the stack and opens them, allowing children to be added.

w.Start(xmlwriter.Elem{Name: "foo"},
	xmlwriter.Elem{Name: "bar"},
	xmlwriter.Elem{Name: "baz"})
w.EndAllFlush()

Becomes: <foo><bar><baz/></bar></foo>

Nodes which have no children, or nodes which can be opened and fully closed with only a trivial amount of information, can be passed to `Writer.Write()`. If written nodes are put on to the stack, they will be popped before Write returns.

w.Write(xmlwriter.Elem{Name: "foo"},
	xmlwriter.Elem{Name: "bar"},
	xmlwriter.Elem{Name: "baz"})

Becomes: <foo/><bar/><baz/>

Block takes a Startable and a variable number of Writable nodes. The Startable will be opened, the Writables will be written, then the Startable will be closed:

w.Block(xmlwriter.Elem{Name: "foo"},
	xmlwriter.Comment{"comment!"},
	xmlwriter.CData{"cdata."},
	xmlwriter.Elem{Name: "bar"},
)

Becomes:

<foo><!--comment!--><![CDATA[cdata.]><bar/></foo>

End

There are several ways to end an element. Choose the End that's right for you!

  • EndAll()
  • EndAllFlush()
  • EndAny()
  • End(NodeKind)
  • End(NodeKind, name...)
  • EndToDepth(int, NodeKind)
  • EndToDepth(int, NodeKind, name...)
  • EndDoc()
  • End...() Where ... is the name of a startable node struct, End ends that node kind. The following two are equivalent: EndElem() End(ElemNode)

Nodes

Nodes as they are written can be in three states: StateOpen, StateOpened or StateEnd. StateOpen == "<elem". StateOpened == "<elem>". StateEnd == "<elem></elem>".

Node structs are available for writing in the following hierarchy. Nodes which are "Startable" (passed to `writer.Start(n)`) are marked with an S. Nodes which are "Writable" (passed to `writer.Write(n)`) are marked with a W.

- xmlwriter.Raw* (W) - xmlwriter.Doc (S)

  • xmlwriter.DTD (S)
  • xmlwriter.DTDEntity (W)
  • xmlwriter.DTDElement (W)
  • xmlwriter.DTDAttrList (S, W)
  • xmlwriter.DTDAttr (W)
  • xmlwriter.Notation (W)
  • xmlwriter.Comment (S, W)
  • xmlwriter.PI (W)
  • xmlwriter.Comment (S, W)
  • xmlwriter.Elem (S, W)
  • xmlwriter.Attr (W)
  • xmlwriter.PI (W)
  • xmlwriter.Text (W)
  • xmlwriter.Comment (S, W)
  • xmlwriter.CommentContent (W)
  • xmlwriter.CData (S, W)
  • xmlwriter.CDataContent (W)

* `xmlwriter.Raw` can be written anywhere, at any time. If a node is in the "open" state but not in the "opened" state, for example you have started an element and written an attribute, writing "raw" will add the content to the inside of the element opening tag unless you call `w.Next()`.

Every node has a corresponding NodeKind constant, which can be found by affixing "Node" to the struct name, i.e. "xmlwriter.Elem" becomes "xmlwriter.ElemNode". These are used for calls to Writer.End().

xmlwriter.Attr{} values can be assigned from any golang primitive like so:

Attr{Name: "foo"}.Int(5)
Attr{Name: "foo"}.Uint64(5)
Attr{Name: "foo"}.Float64(1.234)

Encodings

xmlwriter supports encoders from the golang.org/x/text/encoding package. UTF-8 strings written in from golang will be converted on the fly and the document declaration will be written correctly.

To write your XML using the windows-1252 encoder:

b := &bytes.Buffer{}
enc := charmap.Windows1252.NewEncoder()
w := xw.OpenEncoding(b, "windows-1252", enc)
xw.Must(w.Start(xw.Doc{}))
xw.Must(w.Start(xw.Elem{Name: "hello"}))
xw.Must(w.Write(xw.Text("Résumé")))
w.EndAllFlush()
os.Stdout.Write(b.Bytes())

The document line will look like this:

<?xml version="1.0" encoding="windows-1252"?>

Index

Constants

View Source
const (
	// DTDElemEmpty is a DTDElem Decl used when the element is empty.
	DTDElemEmpty = "EMPTY"

	// DTDElemAny is a DTDElem Decl used when the element can contain any element.
	DTDElemAny = "ANY"

	// DTDElemPCData is a DTDElem Decl used when the element can contain parsed
	// character data.
	DTDElemPCData = "#PCDATA"
)

Variables

This section is empty.

Functions

func CheckChars

func CheckChars(chars string, strict bool) error

CheckChars ensures a string contains characters which are valid in a Text{} node: https://www.w3.org/TR/xml/#NT-Char The 'strict' argument (which xmlwriter should activate by default) ensures that unicode characters referenced in the note are also excluded.

func CheckEncoding

func CheckEncoding(encoding string) error

CheckEncoding validates the characters in a Doc{} node's encoding="..." attribute based on the following production rule:

[A-Za-z] ([A-Za-z0-9._] | '-')*

func CheckName

func CheckName(name string) error

CheckName ensures a string satisfies the following production rules: https://www.w3.org/TR/xml/#NT-NameStartChar, with the exception that it does not return an error on an empty string.

func CheckPubID

func CheckPubID(pubid string) error

CheckPubID validates a string according to the following production rule: https://www.w3.org/TR/xml/#NT-PubidLiteral

Types

type Attr

type Attr struct {
	Prefix string
	URI    string
	Name   string
	Value  string
}

Attr represents an XML attribute to be written by the Writer.

func (Attr) Bool

func (a Attr) Bool(v bool) Attr

Bool writes a boolean to an attribute.

func (Attr) Float32

func (a Attr) Float32(v float32) Attr

Float32 writes a float32 to an attribute.

func (Attr) Float64

func (a Attr) Float64(v float64) Attr

Float64 writes a float64 to an attribute.

func (Attr) Int

func (a Attr) Int(v int) Attr

Int writes an int to an attribute.

func (Attr) Int16

func (a Attr) Int16(v int16) Attr

Int16 writes an int16 to an attribute.

func (Attr) Int32

func (a Attr) Int32(v int32) Attr

Int32 writes an int32 to an attribute.

func (Attr) Int64

func (a Attr) Int64(v int64) Attr

Int64 writes an int64 to an attribute.

func (Attr) Int8

func (a Attr) Int8(v int8) Attr

Int8 writes an int8 to an attribute.

func (Attr) Uint

func (a Attr) Uint(v int) Attr

Uint writes a uint to an attribute.

func (Attr) Uint16

func (a Attr) Uint16(v uint16) Attr

Uint16 writes a uint16 to an attribute.

func (Attr) Uint32

func (a Attr) Uint32(v uint32) Attr

Uint32 writes a uint32 to an attribute.

func (Attr) Uint64

func (a Attr) Uint64(v uint64) Attr

Uint64 writes a uint64 to an attribute.

func (Attr) Uint8

func (a Attr) Uint8(v uint8) Attr

Uint8 writes a uint8 to an attribute.

type CData

type CData struct {
	Content string
}

CData represents an XML CData section which can be written or started by the Writer.

type CDataContent

type CDataContent string

CDataContent represents a text portion of an XML CData section which can be written after a CData is Started.

type Comment

type Comment struct {
	Content string
}

Comment represents an XML comment section which can be written or started by the Writer.

type CommentContent

type CommentContent string

CommentContent represents a text portion of an XML comment which can be written after a Comment is Started.

type DTD

type DTD struct {
	Name     string
	PublicID string
	SystemID string
}

DTD represents a Document Type Definition to be written by the Writer.

type DTDAttList

type DTDAttList struct {
	Name  string
	Attrs []DTDAttr
}

DTDAttList represents a DTD attribute list to be written by the Writer.

Examples:

DTDAttList{Name: "yep", Attrs: []DTDAttr{
	{Name: "a1", Type: DTDAttrString, Required: true},
	{Name: "a2", Type: DTDAttrString, Required: true},
}}))
<!ATTLIST yep a1 CDATA #REQUIRED a2 CDATA #REQUIRED>

type DTDAttr

type DTDAttr struct {
	Name    string
	Type    DTDAttrType
	Default DTDAttrDefaultType
	Value   string
}

DTDAttr represents a DTD attribute to be written by the Writer.

type DTDAttrDefaultType

type DTDAttrDefaultType int

DTDAttrDefaultType represents the possible values from the DefaultDecl production in the DTD spec: https://www.w3.org/TR/REC-xml/#NT-DefaultDecl

const (
	// If Value is empty, this attribute is DTDAttrImplied, otherwise it will be a
	// standard DTDAttrDefault value (without #FIXED). If you need to represent the
	// empty string as your default value, you will need to explicitly use DTDAttrDefault
	// or DTDAttrFixed.
	DTDAttrInfer DTDAttrDefaultType = iota

	// DTDAttr.Value represents the default for this attribute. Value may be an
	// empty string.
	DTDAttrDefault

	// The attribute MUST always be provided. DTDAttr.Value must be empty.
	DTDAttrRequired

	// No default value is provided. DTDAttr.Value must be empty.
	DTDAttrImplied

	// The #FIXED keyword states that the attribute MUST always have the default value.
	// If the declaration is neither #REQUIRED nor #IMPLIED, then the AttValue value
	// contains the declared default value.
	DTDAttrFixed
)

type DTDAttrType

type DTDAttrType string

DTDAttrType constrains the valid values for the Type property of the DTDAttr struct.

const (
	DTDAttrString   DTDAttrType = "CDATA"
	DTDAttrID       DTDAttrType = "ID"
	DTDAttrIDRef    DTDAttrType = "IDREF"
	DTDAttrIDRefs   DTDAttrType = "IDREFS"
	DTDAttrEntity   DTDAttrType = "ENTITY"
	DTDAttrEntities DTDAttrType = "ENTITIES"
	DTDAttrNmtoken  DTDAttrType = "NMTOKEN"
	DTDAttrNmtokens DTDAttrType = "NMTOKENS"
)

type DTDElem

type DTDElem struct {
	Name string
	Decl string
}

DTDElem represents a DTD element definition to be written by the Writer.

Examples:

DTDElem{Name: "elem", Decl: DTDElemEmpty} -> <!ELEMENT elem EMPTY>
DTDElem{Name: "elem", Decl: "(a|b)*"}     -> <!ELEMENT elem (a|b)*>

type DTDEntity

type DTDEntity struct {
	Name     string
	Content  string
	IsPE     bool
	PublicID string
	SystemID string
	NDataID  string
}

DTDEntity represents a DTD entity definition to be written by the Writer.

Examples:

	DTDEntity{Name: "pants", Content: "&#62;"}
	<!ENTITY pants "&#62">

	DTDEntity{Name: "pants", SystemID: "sys"}
	<!ENTITY pants SYSTEM "sys">

	DTDEntity{Name: "pants", SystemID: "sys", IsPE: true}
	<!ENTITY % pants SYSTEM "sys">

	DTDEntity{Name: "pants", SystemID: "sys", PublicID: "pub", NDataID: "nd"}
 <!ENTITY pants PUBLIC "pub" "sys" NDATA nd>

type Doc

type Doc struct {
	// Do not print 'encoding="..."' into the document opening
	SuppressEncoding bool

	// Override Writer.Encoding with this string if not nil
	ForcedEncoding *string

	// Do not print 'version="..."' into the document opening
	SuppressVersion bool

	// Override Writer.Version with this string if not nil
	ForcedVersion *string

	// If nil, do not print 'standalone="..."'
	Standalone *bool
}

Doc represents an XML document which can be started by the writer.

Examples (assuming UTF-8 encoding used with Writer):

Doc{}
<?xml version="1.0" encoding="UTF-8"?>

Doc{}.WithStandalone(false)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>

Doc{}.WithStandalone(true)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

Doc{SuppressVersion: true, SuppressEncoding: true}
<?xml ?>

Doc{}.ForceVersion("pants").ForceEncoding("pants")
<?xml version="pants" encoding="pants" ?>

func (Doc) ForceEncoding

func (d Doc) ForceEncoding(v string) Doc

ForceEncoding is a fluent convenience function for assigning a non-pointer string to Doc.ForcedEncoding.

func (Doc) ForceVersion

func (d Doc) ForceVersion(v string) Doc

ForceVersion is a fluent convenience function for assigning a non-pointer string to Doc.ForcedVersion.

func (Doc) WithStandalone

func (d Doc) WithStandalone(v bool) Doc

WithStandalone is a fluent convenience function for assigning a non-pointer bool to Doc.Standalone

type Elem

type Elem struct {
	Prefix string
	URI    string
	Name   string
	Attrs  []Attr

	// A tree of writable nodes can be assigned to this - they will
	// be written when the node is opened. Not heap-friendly.
	Content []Writable

	// When ending this element, use the full style, i.e. <foo></foo>
	// even if the element is empty.
	Full bool
	// contains filtered or unexported fields
}

Elem represents an XML element to be written by the writer.

type ErrCollector

type ErrCollector struct {
	File  string
	Line  int
	Index int
	Err   error
}

ErrCollector allows you to defer raising or accumulating an error until after a series of procedural calls.

ErrCollector it is intended to help cut down on boilerplate like this:

if err := w.Start(xmlwriter.Doc{}); err != nil {
	return err
}
if err := w.Start(xmlwriter.Elem{Name: "elem"}); err != nil {
	return err
}
if err := w.Start(xmlwriter.Attr{Name: "attr", Value: "yep"}); err != nil {
	return err
}
if err := w.Start(xmlwriter.Attr{Name: "attr2", Value: "nup"}); err != nil {
	return err
}

For any sufficiently complex procedural XML assembly, this is patently ridiculous. ErrCollector allows you to assume that it's ok to keep writing until the end of a controlled block, then fail with the first error that occurred. In complex procedures, ErrCollector is far more succinct and mirrors an idiom used internally in the library, which was itself cribbed from the stdlib's xml package (see cachedWriteError).

For functions that return an error:

func pants(w *xmlwriter.Writer) (err error) {
	ec := &xmlwriter.ErrCollector{}
	defer ec.Set(&err)
	ec.Do(
		w.Start(xmlwriter.Doc{}),
		w.Start(xmlwriter.Elem{Name: "elem"}),
		w.Start(xmlwriter.Attr{Name: "elem", Value: "yep"}),
		w.Start(xmlwriter.Attr{Name: "elem", Value: "nup"}),
	)
	return
}

If you want to panic instead, just substitute `defer ec.Set(&err)` with `defer ec.Panic()`

It is entirely the responsibility of the library's user to remember to call either `ec.Set()` or `ec.Panic()`. If you don't, you'll be swallowing errors.

func (*ErrCollector) Do

func (e *ErrCollector) Do(errs ...error)

Do collects the first error in a list of errors and holds on to it.

If you pass the result of multiple functions to Do, they will not be short circuited on failure - the first error is retained by the collector and the rest are discarded. It is only intended to be used when you know that subsequent calls after the first error are safe to make.

func (*ErrCollector) Error

func (e *ErrCollector) Error() string

Error implements the error interface.

func (*ErrCollector) Must

func (e *ErrCollector) Must(errs ...error)

Must collects the first error in a list of errors and panics with it.

func (*ErrCollector) Panic

func (e *ErrCollector) Panic()

Panic causes the collector to panic if any error has been collected.

This should be called in a defer:

func pants() {
	ec := &xmlwriter.ErrCollector{}
	defer ec.Panic()
	ec.Do(fmt.Errorf("this will panic at the end"))
	fmt.Printf("This will print")
}

func (*ErrCollector) Set

func (e *ErrCollector) Set(err *error)

Set assigns the collector's internal error to an external error variable.

This should be called in a defer with a named return to allow an error to be easily returned if one is collected:

func pants() (err error) {
	ec := &xmlwriter.ErrCollector{}
	defer ec.Set(&err)
	ec.Do(fmt.Errorf("this error will be returned by the pants function"))
	fmt.Printf("This will print")
}

func (*ErrCollector) Unwrap

func (e *ErrCollector) Unwrap() error

Unwrap satisfies the requirement of `errors.Unwrap()`, to allow testing ErrCollector with e.g. `errors.Is()` and `errors.As()`.

type Event

type Event struct {
	State    NodeState
	Node     NodeKind
	Children int
}

Event is raised when a node changes state in the writer. It is currently only relevant to the Indenter but may become an event system in a later version.

func (Event) String

func (e Event) String() string

type Indenter

type Indenter interface {
	// Indent is called every time a node transitions from one State
	// to another. This allows whitespace (or anything, really) to be
	// injected in between any two pieces that the writer writes to the
	// document.
	Indent(w *Writer, last Event, next Event) error

	// Wrap is called on every Text and CommentContent node - if you
	// want your indenter to place text slabs into nicely formatted
	// blocks, the logic goes here.
	Wrap(content string) string
}

Indenter allows custom indenting strategies to be written for pretty printing the resultant XML.

Writing one of these little nightmares is not for the faint of heart - it's a monumental pain to get indenting rules right. See StandardIndenter for a glimpse into the abyss.

type Node

type Node interface {
	// contains filtered or unexported methods
}

Node represents an item which the Writer can Start and/or Write. It is the wider type for Startable and Writable.

type NodeKind

type NodeKind int

NodeKind is the kind of the node. Yep.

const (
	NoNode NodeKind = iota
	AttrNode
	CDataNode
	CDataContentNode
	CommentNode
	CommentContentNode
	DTDNode
	DTDAttListNode
	DTDAttrNode
	DTDElemNode
	DTDEntityNode
	DocNode
	ElemNode
	NotationNode
	PINode
	RawNode
	TextNode
)

Range of allowed NodeKind values.

func (NodeKind) Name

func (n NodeKind) Name() string

Name returns a stable name for the NodeKind. If the NodeKind is invalid, the Name() will be empty. String() returns a human-readable representation for information purposes; if a stable string is required, use this instead.

func (NodeKind) String

func (n NodeKind) String() string

String returns a human-readable representation of the NodeKind. If a stable string is required, use Name().

type NodeState

type NodeState int

NodeState is the state of the node being written.

const (
	// StateOpen indicates the node is open but not opened, e.g. "<elem"
	StateOpen NodeState = iota

	// StateOpened indicates the node is fully opened, e.g. "<elem>"
	StateOpened

	// StateEnded indicates the node's end, e.g. "</elem>"
	StateEnded
)

func (NodeState) Name

func (n NodeState) Name() string

Name returns a string representation fo the NodeState.

type Notation

type Notation struct {
	Name     string
	SystemID string
	PublicID string
}

Notation represents an XML notation declaration to be written by the Writer. https://www.w3.org/TR/xml/#dt-notation

type Option

type Option func(w *Writer)

Option is an option to the Writer.

func WithIndent

func WithIndent() Option

WithIndent sets the Writer up to indent XML elements to make them easier to read using the StandardIndenter:

w := xmlwriter.Open(b, xmlwriter.WithIndent())

func WithIndentString

func WithIndentString(indent string) Option

WithIndentString configures the Writer with a StandardIndenter using a specific indent string:

w := xmlwriter.Open(b, xmlwriter.WithIndentString("    "))

type PI

type PI struct {
	Target  string
	Content string
}

PI represents an XML processing instruction to be written by the Writer.

type Raw

type Raw string

Raw represents a raw string to be written by the Writer. See Writer.WriteRaw()

type StandardIndenter

type StandardIndenter struct {
	// Output whitespace control
	IndentString string
	// contains filtered or unexported fields
}

StandardIndenter implements a primitive Indenter strategy for pretty printing the Writer's XML output.

StandardIndenter is used by the WithIndent writer option:

w := xmlwriter.Open(b, xmlwriter.WithIndent())

func NewStandardIndenter

func NewStandardIndenter() *StandardIndenter

NewStandardIndenter creates a StandardIndenter.

func (*StandardIndenter) Indent

func (s *StandardIndenter) Indent(w *Writer, last Event, next Event) error

Indent satisfies the Indenter interface.

func (*StandardIndenter) Wrap

func (s *StandardIndenter) Wrap(content string) string

Wrap satisfies the Indenter interface.

type Startable

type Startable interface {
	Node
	// contains filtered or unexported methods
}

Startable is a node which can be passed to xmlwriter.Start() - any node which can have children is Startable.

type Text

type Text string

Text represents an XML text section to be written by the Writer. See Writer.WriteText()

type Writable

type Writable interface {
	Node
	// contains filtered or unexported methods
}

Writable is a node which can be passed to xmlwriter.Write(). Writables may or may not have children. Writable nodes can not be passed to xmlwriter.Start().

type Writer

type Writer struct {

	// Perform validation on output. Defaults to true when created using Open().
	Enforce bool

	// When enforcing and checking characters, excludes an additional set of
	// compatibility chars.  Defaults to true. See check.go/CheckChars.
	StrictChars bool

	// Version is placed into the `version="..."` attribute when writing a Doc{}.
	// Defaults to 1.0.
	Version string

	// Determines how much memory the internal buffer will use. Set to 0 to use
	// the default.
	InitialBufSize int

	// Defaults to \n.
	NewlineString string

	// Controls the indenting process used by the writer.
	Indenter Indenter
	// contains filtered or unexported fields
}

Writer writes XML to an io.Writer.

func Open

func Open(w io.Writer, options ...Option) *Writer

Open opens the Writer using the UTF-8 encoding.

func OpenEncoding

func OpenEncoding(w io.Writer, encstr string, encoder *encoding.Encoder, options ...Option) *Writer

OpenEncoding opens the Writer using the supplied encoding.

This example opens an XML writer using the utf16-be encoding:

enc := unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM).NewEncoder()
w := xmlwriter.OpenEncoding(b, "utf-16be", enc)

You should still write UTF-8 strings to the writer - they are converted on the fly to the target encoding.

func (*Writer) Block

func (w *Writer) Block(start Startable, nodes ...Writable) error

Block is a convenience function that takes a parent node and a list of direct children. The parent node is passed to Writer.Start(), the children are passed to Write(), then the parent passed to End().

func (*Writer) Depth

func (w *Writer) Depth() int

Depth returns the number of opened Startable nodes on the stack.

func (*Writer) End

func (w *Writer) End(kind NodeKind, name ...string) error

End ends the current node if it is of the kind specified, and also optionally (and if relevant) if the name matches. If one string is passed to name, it is compared to the node's Name field. If two strings are passed to name, the first is compared to the node's Prefix field, the second is compared to the node's Name field. This form works with the following node types: ElemNode, DTDNode, DTDAttListNode.

func (*Writer) EndAll

func (w *Writer) EndAll() error

EndAll ends every node on the stack

func (*Writer) EndAllFlush

func (w *Writer) EndAllFlush() error

EndAllFlush ends every node on the stack and calls Flush()

func (*Writer) EndAny

func (w *Writer) EndAny() error

EndAny ends the current node, regardless of what kind of node it is.

func (*Writer) EndCData

func (w *Writer) EndCData() (err error)

EndCData pops a CData node from the writer's stack, or returns an error if the current node is not a CData.

func (*Writer) EndComment

func (w *Writer) EndComment() (err error)

EndComment pops a Comment node from the writer's stack, or returns an error if the current node is not a Comment.

func (*Writer) EndDTD

func (w *Writer) EndDTD() (err error)

EndDTD pops a DTD node from the writer's stack, or returns an error if the current node is not a DTD.

func (*Writer) EndDTDAttList

func (w *Writer) EndDTDAttList() (err error)

EndDTDAttList pops a DTDAttList node from the writer's stack, or returns an error if the current node is not a DTDAttList.

func (*Writer) EndDoc

func (w *Writer) EndDoc() (err error)

EndDoc ends the current xmlwriter.Doc{} and any node in between. This is not the same as calling End(DocNode), which will only end a Doc{} if it is the current node on the stack.

func (*Writer) EndElem

func (w *Writer) EndElem(name ...string) error

EndElem pops an Elem node from the writer's stack, or returns an error if the current node is not an Elem. If the Elem has had no children written, it will be closed using the short close style: "<tag/>"

func (*Writer) EndElemFull

func (w *Writer) EndElemFull(name ...string) error

EndElemFull pops an Elem node from the writer's stack, or returns an error if the current node is not an Elem. It will always be closed using the full element close style even if it contains no children: "<tag></tag>".

func (*Writer) EndToDepth

func (w *Writer) EndToDepth(depth int, kind NodeKind, name ...string) error

EndToDepth ends all nodes in the stack up to the supplied depth. The last node must match NodeKind and, if provided and applicable for the node type, the name. This is useful if you want to ensure that everything you open inside a particular scope is closed at the end:

	func outer() {
		w.Start(Elem{Name: "pants"})
		inner()
     // result:
     // <pants><foo><bar/></foo>
	}
	func inner() {
		d := w.Depth()
		defer w.EndToDepth(d, NodeElem)
		w.Start(Elem{Name: "foo"})
		w.Start(Elem{Name: "bar"})
	}

func (*Writer) Flush

func (w *Writer) Flush() error

Flush ensures the output buffer accumuated inside the Writer is fully written to the underlying io.Writer.

func (*Writer) Next

func (w *Writer) Next() error

Next ensures that the current element is opened and the next one can be started. It is called for all node types except Raw{}. This is so that you can write raw strings inside elements that are open but not opened. See StateOpen and StateOpened for more details on this distinction.

func (*Writer) Start

func (w *Writer) Start(nodes ...Startable) error

Start starts a startable node.

func (*Writer) StartCData

func (w *Writer) StartCData(cdata CData) error

StartCData pushes an XML CData node onto the writer's stack. WriteCDataContent can be used to write contents.

func (*Writer) StartComment

func (w *Writer) StartComment(comment Comment) error

StartComment pushes an XML comment node onto the writer's stack. WriteCommentContent can be used to write contents.

func (*Writer) StartDTD

func (w *Writer) StartDTD(dtd DTD) error

StartDTD pushes a Document Type Declaration node onto the writer's stack.

func (*Writer) StartDTDAttList

func (w *Writer) StartDTDAttList(al DTDAttList) error

StartDTDAttList pushes a Document Type Declaration node onto the writer's stack.

func (*Writer) StartDoc

func (w *Writer) StartDoc(doc Doc) error

StartDoc pushes an XML document node onto the writer's stack.

func (*Writer) StartElem

func (w *Writer) StartElem(elem Elem) error

StartElem pushes an XML element node onto the writer's stack.

func (*Writer) Write

func (w *Writer) Write(nodes ...Writable) error

Write writes writable nodes.

func (*Writer) WriteAttr

func (w *Writer) WriteAttr(attrs ...Attr) (err error)

WriteAttr writes one or more XML element attributes to the output.

func (*Writer) WriteCData

func (w *Writer) WriteCData(cdata CData) (err error)

WriteCData writes a complete XML CData section. It can be written inside an Elem or as a top-level node.

func (*Writer) WriteCDataContent

func (w *Writer) WriteCDataContent(cdata string) error

WriteCDataContent writes text inside an already-started XML CData node.

func (*Writer) WriteComment

func (w *Writer) WriteComment(comment Comment) (err error)

WriteComment writes a complete XML Comment section. It can be written inside an Elem, a DTD, a Doc, or as a top-level node.

func (*Writer) WriteCommentContent

func (w *Writer) WriteCommentContent(comment string) error

WriteCommentContent writes text inside an already-started XML Comment node.

func (*Writer) WriteDTDAttList

func (w *Writer) WriteDTDAttList(attlist DTDAttList) (err error)

WriteDTDAttList writes a DTD Attribute List to the output. It can be written inside a DTD or as a top-level node.

func (*Writer) WriteDTDAttr

func (w *Writer) WriteDTDAttr(attr DTDAttr) (err error)

WriteDTDAttr writes a DTD Attribute definition to the output. It can be written inside a DTDAttList or as a top-level node.

func (*Writer) WriteDTDElem

func (w *Writer) WriteDTDElem(el DTDElem) error

WriteDTDElem writes a DTD Element definition to the output. It can be written inside a DTD or as a top-level node.

func (*Writer) WriteDTDEntity

func (w *Writer) WriteDTDEntity(entity DTDEntity) error

WriteDTDEntity writes a DTD Entity definition to the output. It can be written inside a DTD or as a top-level node.

func (*Writer) WriteElem

func (w *Writer) WriteElem(elem Elem) (err error)

WriteElem writes a complete XML Element. It can be written inside an Elem, a Doc, or as a top-level node.

Nested elements and attributes can be written by assigning to the members of the Elem struct to an arbitrary depth (though be warned that this can cause heap escapes):

e := Elem{
	Name: "outer",
	Attrs: []Attr{Attr{Name: "key", Value: "val"}},
	Content: []Writable{Elem{Name: "inner"}},
}

func (*Writer) WriteNotation

func (w *Writer) WriteNotation(n Notation) (err error)

WriteNotation writes an XML notation to the output. It can be written inside a DTD or as a top-level node.

func (*Writer) WritePI

func (w *Writer) WritePI(pi PI) error

WritePI writes an XML processing instruction to the output. It can be written inside a Doc, an Elem or as a top-level node.

func (*Writer) WriteRaw

func (w *Writer) WriteRaw(raw string) error

WriteRaw writes a raw string to the output. This can be any string whatsoever - it does not have to be valid XML and will be written exactly as it is declared. Raw nodes can be written at any stage of the writing process.

func (*Writer) WriteText

func (w *Writer) WriteText(text string) (err error)

WriteText writes an XML text node to the output. It will be appropriately escaped. It can be written inside an Elem or as a top-level node.

Directories

Path Synopsis
tester

Jump to

Keyboard shortcuts

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