xslt

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package xslt implements XSLT 2.0 transformation over the xdm data model, using the xpath package for expression evaluation.

Index

Constants

View Source
const DefaultMaxDepth = 1000

DefaultMaxDepth bounds template recursion when TransformOptions.MaxDepth is zero. It matches xdm.DefaultMaxDepth so that a document the parser accepts is one an identity transform can copy: the recursion counted here is the ordinary descent through the tree, not only a stylesheet calling itself.

Variables

This section is empty.

Functions

This section is empty.

Types

type CompileOptions

type CompileOptions struct {
	// Resolver loads xsl:include and xsl:import targets. Nil disables them,
	// which is the safe default for untrusted stylesheets.
	Resolver ModuleResolver
	// BaseURI of the stylesheet, for resolving relative include paths.
	BaseURI string
	// StaticParams supplies values for top-level xsl:param at compile time.
	StaticParams map[string]string

	// SchemaResolver loads the schemas named by xsl:import-schema. Nil
	// disables loading by location, for the same reason a nil Resolver
	// disables xsl:include: following a location means fetching whatever
	// the stylesheet names. An inline <xs:schema> child needs no resolver.
	SchemaResolver xsd.Resolver
}

CompileOptions configures compilation.

type DecimalFormat

type DecimalFormat struct {
	Name              xdm.QName
	DecimalSeparator  rune
	GroupingSeparator rune
	Percent           rune
	PerMille          rune
	ZeroDigit         rune
	Digit             rune
	PatternSeparator  rune
	MinusSign         rune
	Infinity          string
	NaN               string
}

DecimalFormat holds an xsl:decimal-format declaration.

Every symbol is configurable because the instruction exists to serve locales: a German invoice writes 1.234,56 where an English one writes 1,234.56, and the picture string is written once against whatever symbols the format declares.

type FileResolver

type FileResolver struct {
	// Roots are the directories a relative or absolute path may resolve
	// inside. A path escaping all of them is refused.
	Roots []string
	// contains filtered or unexported fields
}

FileResolver loads stylesheet modules and documents from the filesystem, confined to a set of allowed directories.

The confinement is the point. A stylesheet that can call document() on any path is a file-disclosure primitive, and one that can reach http:// is an SSRF primitive — but a blanket deny is not workable either, because real rule sets load code lists that ship beside the stylesheet. Naming the directories makes the trust boundary explicit and auditable.

func NewFileResolver

func NewFileResolver(roots ...string) (*FileResolver, error)

NewFileResolver returns a resolver confined to the given directories.

func (*FileResolver) ResolveDocument

func (r *FileResolver) ResolveDocument(uri, base string) (*xdm.Tree, error)

ResolveDocument implements xpath.DocumentResolver for fn:doc and fn:document.

func (*FileResolver) ResolveModule

func (r *FileResolver) ResolveModule(href, base string) (*xdm.Node, string, error)

ResolveModule implements ModuleResolver for xsl:include and xsl:import.

type Instruction

type Instruction interface {
	// Execute runs the instruction, appending to out.
	Execute(rt *runtime, out *outputBuilder) error
}

Instruction is one compiled XSLT instruction.

Instructions write to an output builder rather than returning values, because an XSLT sequence constructor produces a *stream* of nodes and atomic values: xsl:element opens a node that subsequent instructions add children to. Returning trees from each instruction and concatenating them would mean building and copying the same subtree repeatedly.

type ModuleResolver

type ModuleResolver interface {
	ResolveModule(href, base string) (*xdm.Node, string, error)
}

ModuleResolver loads an included or imported stylesheet module.

type OutputSettings

type OutputSettings struct {
	Method        string // "xml", "html", "text"
	Indent        bool
	OmitXMLDecl   bool
	Encoding      string
	DocTypePublic string
	DocTypeSystem string
	CDataElements []xdm.QName
	Standalone    string
	// Version is xsl:output/@version; "5.0" selects the HTML5 doctype.
	Version string
	// UseCharacterMaps names the xsl:character-map declarations applied at
	// serialisation.
	UseCharacterMaps []xdm.QName
}

OutputSettings holds the xsl:output declaration.

type Pattern

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

Pattern is a compiled xsl:template match pattern.

Patterns look like path expressions but mean something different: a path says "navigate from here", a pattern says "does this node match". The natural implementation of matching — evaluate the path and check membership — is quadratic, because it would visit every node in the document for every node being matched.

Instead a pattern is matched right-to-left from the candidate node: check the last step's node test against the node itself, then walk *up* verifying each preceding step. That makes a match cost O(depth) rather than O(document size), which is the difference between a transform that finishes and one that does not on a large invoice.

func CompilePattern

func CompilePattern(src string, ns xpath.NamespaceResolver) (*Pattern, error)

CompilePattern compiles an XSLT match pattern.

func (*Pattern) Matches

func (p *Pattern) Matches(node *xdm.Node, ctx *xpath.Context) (bool, error)

Matches reports whether node matches the pattern.

ctx supplies the focus for predicate evaluation. Predicates in a pattern are evaluated with the candidate node as the context item, and with a context position derived from its position among its like-named siblings — which is why "para[1]" as a pattern means "a para that is the first para child of its parent" rather than "the first para in the document".

func (*Pattern) Priority

func (p *Pattern) Priority() float64

Priority returns the pattern's default priority, per the XSLT rules: a specific name test scores 0, a namespace wildcard -0.25, a full wildcard or bare kind test -0.5, and anything more complex 0.5.

These numbers exist so that a more specific template wins over a general one without the author having to say so. Getting them wrong makes template selection silently pick the wrong rule, which is far harder to debug than a crash.

func (*Pattern) String

func (p *Pattern) String() string

String returns the pattern source.

type Result

type Result struct {

	// Nodes is the result sequence, which for a typical stylesheet is a
	// single element.
	Nodes xdm.Sequence
	// Messages holds xsl:message output, in the order produced.
	Messages []string
	// Secondary holds the documents produced by xsl:result-document, in the
	// order produced. It is empty for the great majority of stylesheets,
	// which produce a single result.
	Secondary []SecondaryResult
	// contains filtered or unexported fields
}

Result is the outcome of a transform.

func (*Result) Serialize

func (r *Result) Serialize(w io.Writer) error

Serialize writes the result using the stylesheet's xsl:output settings.

Deliberately not named WriteTo: that name implies io.WriterTo, whose contract returns a byte count this would have to fabricate.

func (*Result) String

func (r *Result) String() string

String renders the result using the stylesheet's output settings.

func (*Result) Tree

func (r *Result) Tree() *xdm.Node

Tree returns the result as a document node, for callers that want to keep navigating it rather than serialise it — which is what a Schematron driver does with an SVRL report.

type SecondaryResult

type SecondaryResult struct {
	// Href is the resolved @href value, as written by the stylesheet. It is
	// the caller's choice what to do with it — this engine never writes to
	// the filesystem on a stylesheet's behalf, since a transform that can
	// create files anywhere the process can write is a hazard the caller
	// should be the one to opt into.
	Href string
	// Nodes is the result sequence for this document.
	Nodes xdm.Sequence
	// Output holds the serialisation settings that apply to this document,
	// taken from @format and any serialisation attributes on the instruction.
	Output OutputSettings
}

SecondaryResult is one document produced by xsl:result-document.

It is kept separate from the principal result rather than merged into it: the whole point of the instruction is that the stylesheet author wants two distinct documents, and folding them together would give a caller expecting several outputs a single plausible-looking wrong one.

func (*SecondaryResult) Serialize

func (sr *SecondaryResult) Serialize(w io.Writer, charMap map[rune]string) error

Serialize writes the secondary document using its own output settings.

A caller holding a SecondaryResult would otherwise have no way to render it: the serialiser is unexported, and re-deriving these settings from the stylesheet is exactly the duplication @format exists to avoid.

func (*SecondaryResult) String

func (sr *SecondaryResult) String() string

String renders the secondary document using its own output settings.

type Stylesheet

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

Stylesheet is a compiled XSLT 2.0 stylesheet.

Compilation is separated from execution so that a stylesheet compiles once and transforms many documents concurrently. Everything reachable from here is immutable after Compile returns; all per-transform state lives in the runtime context. That is what makes a compiled EN 16931 rule set — tens of megabytes — shareable rather than per-worker.

func Compile

func Compile(doc *xdm.Node, opts CompileOptions) (*Stylesheet, error)

Compile compiles a stylesheet from a parsed XSLT document.

func (*Stylesheet) Output

func (s *Stylesheet) Output() OutputSettings

Output returns the stylesheet's output settings.

func (*Stylesheet) Schema

func (s *Stylesheet) Schema() *xsd.Schema

Schema returns the schema assembled from the stylesheet's xsl:import-schema declarations, or nil when it has none.

It is exposed so that a caller can validate a source document against the same schema the stylesheet declares, rather than having to load it twice and risk the two disagreeing.

func (*Stylesheet) Transform

func (s *Stylesheet) Transform(ctx context.Context, source *xdm.Node, opts TransformOptions) (*Result, error)

Transform applies the stylesheet to a source document.

The Stylesheet is not mutated, so one compiled stylesheet may be used from many goroutines concurrently.

type Template

type Template struct {
	Match    *Pattern
	Name     xdm.QName
	HasName  bool
	Mode     []string // empty means the default (unnamed) mode
	Priority float64
	Params   []*Variable
	Body     []Instruction
	// contains filtered or unexported fields
}

Template is a compiled xsl:template.

type TransformOptions

type TransformOptions struct {
	// Params supplies values for top-level xsl:param, keyed by Clark name
	// ("{uri}local", or just "local" for a no-namespace parameter).
	Params map[string]xdm.Sequence

	// Documents resolves fn:doc and fn:document. Nil disables them, which is
	// the default: a stylesheet that can open arbitrary URIs is an SSRF and
	// file-disclosure vector, and validation rule sets need at most the code
	// lists shipped beside them.
	Documents xpath.DocumentResolver

	// Collections resolves fn:collection. Nil disables it, which is the
	// default, and setting Documents does not set this: enabling fn:doc for
	// a known code list should not also let a stylesheet enumerate whatever
	// a collection URI happens to name.
	Collections xpath.CollectionResolver

	// MaxDepth bounds template recursion. Zero means DefaultMaxDepth; a
	// negative value means no limit.
	//
	// The bound catches a stylesheet that recurses without a base case,
	// which is the common authoring mistake. But it also counts the ordinary
	// descent of an identity transform, so a limit below the parser's left
	// this refusing documents it had just accepted: at the old fixed 300, a
	// legal 500-deep document could be parsed and not transformed.
	MaxDepth int

	// InitialMode names the mode for the initial apply-templates.
	InitialMode string

	// InitialTemplate names a template to invoke instead of matching the
	// document root, which is how a stylesheet with only named templates is
	// entered.
	InitialTemplate string

	// Now fixes the value fn:current-dateTime returns. Leave it zero to use
	// the wall clock; set it to make a transform reproducible, which is what
	// a golden-file test needs.
	Now time.Time

	// ImplicitTimezone is the offset in minutes for date values with no
	// timezone. Defaults to UTC so that results are reproducible across
	// machines.
	ImplicitTimezone int
}

TransformOptions configures one transform.

type Variable

type Variable struct {
	Name xdm.QName
	// Select is the value expression, or nil when the value comes from the
	// element's content.
	Select *xpath.Compiled
	// Body is the sequence constructor used when Select is absent. A variable
	// with content builds a temporary tree, which is why the two forms are
	// not interchangeable: "select" yields whatever the expression yields,
	// while content always yields a document node.
	Body []Instruction
	// Required marks a parameter that must be supplied.
	Required bool
	// Tunnel marks a tunnel parameter, which passes through templates that do
	// not declare it.
	Tunnel bool
	// AsType is the compiled "as" declaration, applied to the value when
	// present. XSLT converts the value to this type rather than merely
	// checking it, so it changes results and not just error messages.
	AsType *sequenceType
}

Variable is a compiled xsl:variable, xsl:param or xsl:with-param.

Jump to

Keyboard shortcuts

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