xslt
xslt.cr
Production-grade, memory-safe, and secure-by-default XSLT 1.0 and EXSLT transformation library for Crystal.
Wraps native libxslt and libexslt through low-level C FFI bindings seamlessly integrated with Crystal standard library XML::Document and IO.
Features
- Memory Safety & Zero Double-Frees: Deep-clones Crystal GC-managed
XML::Documenttrees (xmlCopyDoc) when compiling stylesheets, eliminating double-free segfaults betweenxsltFreeStylesheetand Crystal Boehm GC finalizers. - Secure Sandboxing by Default: Implements zero-trust sandboxing via
Xslt::SecurityPolicy, strictly forbidding Local File Inclusion (LFI) viadocument()or<xsl:include>, Server-Side Request Forgery (SSRF) over network URIs, arbitrary file writes via<exsl:document>, and unauthorized directory creation. - Full EXSLT Integration: Out-of-the-box support for math (
math:max,math:min,math:sqrt), sets (set:distinct,set:difference), strings (str:tokenize,str:replace), dates (date:year), dynamic expressions (dyn:evaluate), and result tree fragments (exsl:node-set). - Flexible Parameter Handling: Automatic parameter escaping and quoting via
params:and dynamic XPath expression evaluation viaxpath_params:. - Streaming & Multi-Target Serialization: Transform directly to
XML::Document, formattedString, or stream directly into any CrystalIO(e.g.,IO::Memory,File,HTTP::Server::Response). - Thread/Fiber Concurrency Safe: Compiled stylesheets are re-entrant and can be shared across multiple fibers concurrently without locks or data races.
- Zero Stderr Leakage: C-level callbacks intercept all diagnostics cleanly into typed exceptions (
Xslt::ParseError,Xslt::TransformError,Xslt::SecurityError).
System Requirements
Requires libxslt and libxml2 development headers:
# Fedora/RHEL/CentOS
sudo dnf install libxslt-devel libxml2-devel
# Debian/Ubuntu
sudo apt-get install libxslt1-dev libxml2-dev
# Arch Linux
sudo pacman -S libxslt libxml2
Installation
Add the dependency to your shard.yml:
dependencies:
xslt:
gitlab: renich/xslt
Run shards install.
Usage
Quick String Transformation
require "xslt"
xml = "<greeting><message>Hello, World!</message></greeting>"
xsl = <<-XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<xsl:template match="/greeting">
<result><xsl:value-of select="message"/></result>
</xsl:template>
</xsl:stylesheet>
XSL
output = Xslt.transform_to_string(xml, xsl)
# => "<result>Hello, World!</result>"
Precompiled Stylesheets & Re-use
For repeated transformations, precompile stylesheets with Xslt::Stylesheet for maximum throughput:
sheet = Xslt::Stylesheet.new(Path["templates/report.xsl"])
# Transform multiple documents concurrently across fibers
100.times do |i|
spawn do
xml = fetch_xml_for(i)
result = sheet.transform_to_string(xml)
process(result)
end
end
Transforming into XML::Document
doc = sheet.transform(xml)
puts doc.root.try(&.name)
Streaming Directly to IO
Avoid intermediate heap allocations by streaming directly to files, memory buffers, or sockets:
File.open("output.html", "w") do |file|
sheet.transform(xml, file)
end
Parameters & Dynamic XPath
# String parameters are auto-quoted safely
result = sheet.transform_to_string(
xml,
params: {
"title" => "User's Report & Summary",
"section" => "Overview",
}
)
# XPath parameters evaluate dynamic XPath expressions at runtime
result = sheet.transform_to_string(
xml,
xpath_params: {
"multiplier" => "2 * 5",
"flag" => "true()",
"count_expr" => "count(//item)",
}
)
Security Sandboxing & Granular Whitelisting
By default, all external file/network reads, file writes, and directory creation are strictly denied:
# Denied by default -> raises Xslt::SecurityError
begin
Xslt.transform_to_string(xml, malicious_xsl)
rescue ex : Xslt::SecurityError
puts "Access denied to: #{ex.target}"
end
# Selectively whitelist specific read paths or network hosts
policy = Xslt::SecurityPolicy.sandbox(
allowed_read_paths: ["/var/data/catalog.xml", Path["/var/data/shared"]],
allowed_read_hosts: ["api.example.com"],
)
sheet = Xslt::Stylesheet.new(xsl, security_policy: policy)
result = sheet.transform_to_string(xml, security_policy: policy)
Exception Hierarchy
All exceptions inherit from Xslt::Error:
Xslt::Error: Base exception class carrying structured diagnostic messages from LibXSLT/LibXML.Xslt::ParseError: Raised when parsing source XML or compiling XSLT stylesheets fails.Xslt::TransformError: Raised when runtime template execution or dynamic XPath evaluation fails.Xslt::SecurityError: Raised when an operation violates security policy boundaries (operation,target).
Contributing
See CONTRIBUTING.rst for development setup, testing, formatting, and submission guidelines.
Changelog
See CHANGELOG.rst for release history and version notes.
License
MIT License. Copyright (c) 2026 Rénich Bon Ćirić.
xslt
- 0
- 0
- 0
- 0
- 0
- 26 days ago
- October 2, 2026
MIT License
Sun, 04 Oct 2026 20:11:05 GMT