Documentation ¶
Index ¶
- Constants
- Variables
- func Attr[T any](key string, val T) func(AttrWriter)
- func Attrs[M ~map[string]T, T any](m M) func(AttrWriter)
- func ScrambleFunc(s string, f func(byte) bool) []byte
- func Tag(name string, args ...any) func(TagWriter)
- type AttrMarshaler
- type AttrPrinter
- type AttrWriter
- type ContMarshaler
- type ContPrinter
- type ContWriter
- type DeclPrinter
- type ErrUnsupportedType
- type IndentStyle
- type Printer
- type PrinterFlags
- type RawAttr
- type RawCont
- type TagKind
- type TagPrinter
- type TagWriter
- type Writer
Examples ¶
Constants ¶
const ( Block = TagKind(iota) // block level indentation (default) Inline // inline tag )
Accepted values for TagKind:
const ( IndentTabs = IndentStyle(0) // each block level starts on a new line, indented with one '\t' per level Indent2Spaces = IndentStyle(2) // each block level starts on a new line, indented with 2 spaces per level Indent4Spaces = IndentStyle(4) // each block level starts on a new line, indented with 4 spaces per level IndentNone = IndentStyle(-1) // no new lines, no indentation )
const AttrQuotationMark = '\''
const (
PreserveInlineWhitespace = PrinterFlags(1 << iota)
)
Variables ¶
var ErrEmptyAttribute = errors.New("xml: empty sttribute")
Functions ¶
func Attr ¶
func Attr[T any](key string, val T) func(AttrWriter)
func Attrs ¶
func Attrs[M ~map[string]T, T any](m M) func(AttrWriter)
Attrs takes a generic map[string]T and turns it into a functor for writing attributes that can be passed to TagWriter.
func ScrambleFunc ¶
ScrambleFunc is a generic string scrambler that replaces codeunits matched by f with xml character references.
Types ¶
type AttrMarshaler ¶
type AttrPrinter ¶
type AttrPrinter interface { // Attr adds key='val' pairs to a previously opened tag. Notice that Attr works // only immediately after opening the tag, once the opening tag is finalized, // calling Attr will panic. See description of OTag() for more details. Attr(key string, val RawAttr) }
AttrPrinter is an interface for writing XML tag attributes.
type AttrWriter ¶
type AttrWriter interface { // Attr writes key='val' pair into tag's attributes. Accepted val types are: // // - RawAttr, a special version of []byte that is written as-is: // - string, gets scrambled with the ScrambleAttr() function // - nils, or pointer types resolving to nil empty attribute // - types supporting AttrMarshaler interface are resolved as t.MarshalXAttr() // - types supporting encoding.TextMarshaler are marshaled into text, then scrambled with ScrambleAttr() // - boolean types are resolved to 'true' or 'false' // - integer types are resolved to their decimal representation // - floating point types are converted to strings with strconv.FormatFloat using fmt='g' and prec=-1 // - all other types will panic with ErrUnsupportedType Attr(string, any) // OptAttr works similar to Attr, but it will skip writing the whole key='val' // pair if val is empty: // // - RawAttr is considered empty if its length is zero // - strings are considered empty if their length is zero // - nils and pointer types resolving to nil are considered empty // - types that support AttrMarshaler are considered empty if the bool part returned by t.MarshalXAttr() is false // - types that support encoding.TextMarshaler are considered empty if v.MarshalText() returns empty byte slice // - floating/integer/boolean types are never considered empty // - all other types will panic with ErrUnsupportedType OptAttr(string, any) // Attrs writes map[string]any as attributes. The keys in the map are treated as // attribute keys. These are written raw without any scrambling or validation, // make sure you don't pass maps with keys that don't conform to xml attribute // key syntax. Attrs(map[string]any) }
AttrWriter is an interface for writing XML attributes.
type ContMarshaler ¶
type ContMarshaler interface {
MarshalXCont(w Printer)
}
type ContPrinter ¶
type ContPrinter interface { // Content places non-tagged content into the output. For indentation purposes, // this content is considered as inline. Typically, this would be some text // between or inside other tags, but you can also use this for emiting commends, // cdata, and processing instructions. Content(RawCont) // Linebreak can be used for inserting '\n' linebreaks after tags that have // line break semantics, like <br/> tags in html. Linebreak() StopInline() }
ContPrinter is an interface for writing content between XML tags.
type ContWriter ¶
type ContWriter interface {
Content(...any)
}
ContWriter is an interface for writing content between tags.
type DeclPrinter ¶
type DeclPrinter interface { // BOM writes UTF-8 byte order mask. BOM() // XmlDecl writes XmlDecl at the top of the file. XmlDecl() }
DeclPrinter handles generation of top matter in the XML document.
type ErrUnsupportedType ¶
UnsupportedTypeError is returned when Marshal encounters a type that cannot be converted into XML.
func (ErrUnsupportedType) Error ¶
func (e ErrUnsupportedType) Error() string
type Printer ¶
type Printer interface { DeclPrinter AttrPrinter ContPrinter TagPrinter }
Printer combines DeclPrinter, AttrPrinter, ContPrinter, and TagPrinter interfaces into one providing the complete support for XML syntax.
Example ¶
buf := bytes.Buffer{} p := NewPrinter(Indent2Spaces, func(s []byte) { buf.Write(s) }, func(n string) TagKind { if n == "em" || n == "strong" { return Inline } else { return Block } }) p.OTag("root") p.Attr("key", RawAttr("val")) p.OTag("div") p.CTag() p.OTag("h1") p.Content(RawCont("heading")) p.CTag() // p p.OTag("p") p.Content(RawCont("paragraph")) p.CTag() // p p.OTag("p") p.CTag() // p // content block p.Content(RawCont("Hello ")) p.OTag("em") p.Content(RawCont("World!")) p.CTag() // content block p.OTag("strong") p.Content(RawCont("Hello")) p.CTag() p.Content(RawCont(" World!")) p.OTag("p") p.OTag("strong") p.Content(RawCont("bold")) p.CTag() p.CTag() // p p.OTag("div") p.OTag("div") p.CTag() p.CTag() p.OTag("p") // note: this can be turned off with the PreserveInlineWhitespace flag p.Content(RawCont("line breaks must\nalign nicely with additional\nindentation that matches parent's\nblock level")) p.CTag() p.CTag() // root fmt.Println(buf.String())
Output: <root key='val'> <div/> <h1>heading</h1> <p>paragraph</p> <p/> Hello <em>World!</em><strong>Hello</strong> World! <p><strong>bold</strong></p> <div> <div/> </div> <p>line breaks must align nicely with additional indentation that matches parent's block level</p> </root>
func NewPrinter ¶
func NewPrinter(indenter IndentStyle, putter func([]byte), tagger func(string) TagKind) Printer
NewPrinter creates a new Printer for writing XML files.
The tagger parameter is a callback that allows to customize indentation for certain tags. If tagger is nil, then all the tags will be treated as block level tags.
type PrinterFlags ¶
type PrinterFlags uint
type RawAttr ¶
type RawAttr []byte
RawAttr is used when writing XML attribute values to indicate that a value can be written without any further processing (bypasses ScrambleAttr)
func ScrambleAttr ¶
ScrambleAttr is a scrambler for attribute values.
type RawCont ¶
type RawCont []byte
RawCont is used when writing XML content to indicate that it does not need any further processing (bypasses ScrambleCont).
type TagKind ¶
type TagKind int
TagKind is used to customize the behavior of tags when styling the XML document's appearance with automatic indentation.
type TagPrinter ¶
type TagPrinter interface { // OTag starts a new open tag: // // <name // // The name parameter is written verbatim, make sure all symbols in it // conform to XML standards. // // Once a tag is open, you can add attributes to it with the Attr command: // // <name key='val' key2='val2' // // After writing the attributes, you can call CTag, which immediately closes // the tag: // // <name key='val' key2='val2'/> // // Or you can start writing child content with the Content(...) call: // // <name key='val' key2='val2'>... // // Or you can start writing subtags with the another OTag()/CTag() call: // // <name key='val' key2='val2'><subtag/> ... // // All Content() and child OTag()/CTag() calls may be repeated and // interleaved. Once done, call CTag() to finalize the tag: // // <name key='val' key2='val2'> ... child content and subtags ... </name> // // Once done with the tag, a matching CTag() call must be envoked. // OTag(name string) // CTag closes the tag that was previously opened with the OTag() call. // // If there was no content written after opening the tag, you will get // // <tag optional='attributes' /> // // If some content was written, you will get it wrapped in an open/close tag // pair: // // <tag optional='attributes'> ... content ... </tag> // // If you want to have open/close pair with empty content, then make a dummy // empty content call after opening the tag: Content(nil): // // <tag optional='attributes'></tag> // CTag() }
type TagWriter ¶
type TagWriter interface { // Tag writes an XML tag with attributes and content from args, where accepted arguments are: // // - map[string]any - is written into tag attributes // - func(AttrWriter) - is written into tag attribute // - Attrs(map[string]T) - is processed into attributes, wrapped as func(AttrWriter) // - all types supported by ContWriter - written into tag content Tag(string, ...any) }
TagWriter is an interface for writing XML tags with optional attributes and content.
type Writer ¶
type Writer interface { ContWriter TagWriter }
Writer combines AttrWriter and TagWriter
Example ¶
buf := strings.Builder{} p := NewPrinter(Indent2Spaces, func(s []byte) { buf.Write(s) }, func(n string) TagKind { if n == "em" || n == "strong" { return Inline } else { return Block } }) w := NewWriter(p) p.XmlDecl() attrs := map[string]any{ "k1": "v1", "k2": 42, "k3": 3.14, } notanymap := map[string]string{ "s1": "v1", "s2": "v2", "s3": "v3", } w.Tag("root", Attr("key", "value"), Attr("bool", true), Attr("int", 42), // block tag indenting Tag("div"), Tag("div", Tag("subdiv", Tag("subsubdiv"))), // indentation is handled differently for block and inline tags Tag("em"), Tag("em", Tag("em", Tag("em"))), // simple content Tag("h1", "String Content"), // plain content between tags "Plain text content with automatic\ncharacter reference scrambling\nthat also supports aligned wrapping", // inline child tags within content Tag("p", "String content with ", Tag("strong", "inline formatting"), " is handled as expected"), // block child tags within content Tag("p", "String content with", Tag("div", "block subtags"), "is handled differently"), // declarative attributes Tag("div", Attr("key", "val"), "automatically sorts between attributes and content", attrs), Tag("div", Attrs(notanymap), "maps that are not `map[string]any` must be wrapped with Attrs(mymap)"), // functional attributes Tag("div", "functional and subfunctional attribute writing", func(attrs AttrWriter) { attrs.Attr("k", "v") attrs.Attr("subfunc", func(sub AttrWriter) { sub.Attr("k2", "subfunc") }) }), // funcional content func(sub Writer) { sub.Tag("p", "functional content writing") sub.Tag("p", func(subsub Writer) { subsub.Content("can be nested") }) }, // low level printing Tag("div", func(p Printer) { p.Content(nil) // start new line p.Linebreak() p.Content(RawCont("direct raw writing with higher performance")) p.OTag("p") p.Content(RawCont("<!--and and flexibility-->")) p.CTag() p.Content(RawCont("<![CDATA[...]]>")) p.Linebreak() p.Content(ScrambleCont("make sure you pair OTag/CTag calls\nand avoid writing <things> that do not comply with XML syntax")) p.StopInline() // make sure the following block level closing tag is indented and aligned nicely }), ) fmt.Println(buf.String())
Output: <?xml version='1.0' encoding='UTF-8'?> <root key='value' bool='true' int='42'> <div/> <div> <subdiv> <subsubdiv/> </subdiv> </div> <em/><em><em><em/></em></em> <h1>String Content</h1> Plain text content with automatic character reference scrambling that also supports aligned wrapping <p>String content with <strong>inline formatting</strong> is handled as expected</p> <p>String content with <div>block subtags</div> is handled differently</p> <div key='val' k1='v1' k2='42' k3='3.14'>automatically sorts between attributes and content</div> <div s1='v1' s2='v2' s3='v3'>maps that are not `map[string]any` must be wrapped with Attrs(mymap)</div> <div k='v' k2='subfunc'>functional and subfunctional attribute writing</div> <p>functional content writing</p> <p>can be nested</p> <div> direct raw writing with higher performance <p><!--and and flexibility--></p> <![CDATA[...]]> make sure you pair OTag/CTag calls and avoid writing <things> that do not comply with XML syntax </div> </root>