The shim package provides a drop-in replacement for Go's encoding/xml
package backed by helium's parser.
Import path: github.com/lestrrat-go/helium/shim
It exposes the same core API surface as encoding/xml, including
Marshal, Unmarshal, NewEncoder, NewDecoder, Token,
EncodeToken, and the familiar struct tags such as xml:"name,attr",
,chardata, ,innerxml, and ,omitempty.
package examples_test
import (
"fmt"
"github.com/lestrrat-go/helium/shim"
)
func Example_shim_marshal() {
// shim.Marshal works like encoding/xml.Marshal: it serializes a Go
// struct into XML bytes using struct tags.
type Person struct {
XMLName shim.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
}
p := Person{Name: "Alice", Age: 30}
data, err := shim.Marshal(p)
if err != nil {
fmt.Printf("error: %s\n", err)
return
}
fmt.Println(string(data))
// Output:
// <person><name>Alice</name><age>30</age></person>
}source: examples/shim_marshal_example_test.go
Decoder.Strict = falseis not supported.HTMLAutoCloseis omitted andDecoder.AutoCloseis a no-op.- Undeclared namespace prefixes are rejected.
- The helium parser is the single authority for the XML declaration. Its parse
decides the XMLDecl grammar, the version rule, and placement, and shim's
verdict is helium's;
Unmarshal, a reader-backedDecoder, and a TokenReader-backedDecoderagree. - A document declaring a non-UTF-8 encoding (e.g.
UTF-16,ISO-8859-1) is rejected unless aDecoder.CharsetReaderis set to convert it — the same rule asencoding/xml. shim applies it from helium's decoded encoding, so every entry point agrees even when the declaration is itself in a fixed-width Unicode encoding (UTF-16 / UCS-4) that a byte-level scan cannot read. A fixed-width Unicode document that declares no encoding names none and is accepted. - shim accepts the XML versions helium accepts — 1.0 and 1.1 (helium
implements XML 1.1) — where
encoding/xmlrejectsversion="1.1". A version outside the 1.x family (e.g.2.0) is rejected.Unmarshaland the reader-backedDecoderaccept 1.1 directly; a TokenReader-backedDecoderaccepts a 1.1 declaration once delivered as a token, but anencoding/xmldecoder used as the TokenReader cannot deliver one — it rejects 1.1 during its own tokenization, a limitation ofencoding/xml, not shim. - An XML declaration that does not conform to the XMLDecl grammar is rejected
by every entry point: a
charset=pseudo-attribute, a missing or empty version, an empty encoding, astandalonethat is notyes/no, a repeated pseudo-attribute, or pseudo-attributes out of order.encoding/xmlaccepts them all. - An XML declaration is admitted only as the very first thing in the document —
at document position 0, with only a byte-order mark allowed ahead of it. Every
entry point (
Unmarshal, the reader-backedDecoder, and the TokenReader-backedDecoder) rejects a<?xmlpreceded by any leading whitespace, or following an earlier declaration, a comment, a processing instruction, or a doctype;encoding/xmltolerates leading whitespace and reports a later<?xmlas an ordinaryProcInst. Whitespace ahead of the root element (with no declaration) stays accepted — only whitespace ahead of a declaration rejects. - The target
xmlis reserved in any casing (PITarget ::= Name - (('X'|'x')('M'|'m')('L'|'l'))), so<?XML ...?>,<?Xml ...?>and<?xMl ...?>are illegal wherever they appear and are rejected by every entry point. A target that merely begins withxml(<?xmlversion ="2.0"?>,<?xml-stylesheet ...?>) is an ordinary PI, not a declaration, and is accepted. - A declaration with whitespace around the version pseudo-attribute's
=(<?xml version = "2.0"?>) is rejected as an unsupported version;encoding/xmlaccepts it. - Namespace declarations are emitted before regular attributes.
InputOffsetis approximate.- Empty elements in
,innerxmlmay serialize as self-closed tags.