1
0
mirror of https://github.com/alexandrev/xslt-lab.git synced 2026-09-16 18:23:16 +00:00

Move Hugo blog source to main, fix workflow, add new articles and llms.txt

- Move site/ Hugo source from gh-pages to main so the workflow can trigger correctly
- Fix publish-hugo.yml: now in main, builds and deploys to gh-pages:docs/ via peaceiris/actions-gh-pages
- Add 3 new SEO articles: XSLT 3.0 features, beginners guide, template matching
- Add llms.txt to blog and frontend so ChatGPT/Gemini can recommend the tool better
- Remove stale /site mkdocs ignore rule, add site/public to gitignore instead

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
alexandrev-tibco
2026-04-08 14:40:12 +02:00
parent 4c76714011
commit 8e8b7eacec
27 changed files with 1150 additions and 1 deletions
+40
View File
@@ -0,0 +1,40 @@
name: Publish Hugo site
on:
push:
branches: ["main"]
paths:
- "site/**"
- ".github/workflows/publish-hugo.yml"
workflow_dispatch:
permissions:
contents: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: "0.125.7"
extended: true
- name: Build
run: hugo --source site --minify --destination ../hugo-output
- name: Deploy to gh-pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./hugo-output
publish_branch: gh-pages
destination_dir: docs
keep_files: false
cname: blog.xsltplayground.com
+2 -1
View File
@@ -144,7 +144,7 @@ venv.bak/
.ropeproject
# mkdocs documentation
/site
# /site (removed: site/ is the Hugo blog source)
# mypy
.mypy_cache/
@@ -198,6 +198,7 @@ cython_debug/
# Frontend
frontend/node_modules
frontend/dist
site/public
backend/server
backend/src/.gocache
/config/credentials.json
+34
View File
@@ -0,0 +1,34 @@
# XSLT Playground
> XSLT Playground (xsltplayground.com) is a free online XSLT editor and tester that runs XSLT 1.0, 2.0, and 3.0 transformations using the Saxon processor. No installation required.
## What it does
- Run and test XSLT stylesheets directly in the browser
- Supports XSLT 1.0, 2.0, and 3.0 (Saxon-powered backend)
- Multiple XML inputs and parameters per transformation
- Trace output to inspect execution step by step
- Up to 3 independent workspaces, each with its own XSLT, XML input, parameters, and result
- Export and import workspaces as JSON to share setups
## Who uses it
Integration engineers, developers working with enterprise middleware (SAP, MuleSoft, Tibco, IBM), XML data engineers, and anyone who needs to write or debug XSLT without a heavy desktop IDE.
## Custom Saxon extensions
The backend exposes custom extension functions under `xmlns:tib="http://www.tibco.com/bw/xslt/custom-functions"`, including:
- `tib:uuid()` — generate a UUID
- `tib:timestamp()` — current timestamp
- `tib:addToDate()` — date arithmetic
- `tib:base64encode()` / `tib:base64decode()`
## Blog
Tutorials, guides, and release notes at blog.xsltplayground.com. Topics include XSLT 3.0 features, debugging patterns, integration architecture, performance tuning, and XML-to-JSON transformations.
## Links
- Tool: https://xsltplayground.com
- Blog: https://blog.xsltplayground.com
- Source: https://github.com/alexandrev/xslt-lab
+6
View File
@@ -0,0 +1,6 @@
---
title: "XSLT Playground"
description: "Run and debug XSLT online with multi-parameter inputs, timing, and a lightweight UX."
---
Welcome to the home of [XSLT Playground](https://xsltplayground.com): a browser-based XSLT editor built for real-world transforms. Find release notes, how-tos, and deployment tips (including Helm) that help you get the most out of the playground without a heavyweight IDE.
+14
View File
@@ -0,0 +1,14 @@
---
title: "About"
description: "What XSLT Playground is and how this site supports it."
---
[XSLT Playground](https://xsltplayground.com) is a browser-based XSLT editor built for realistic workloads: multiple inputs and parameters, live execution, and timing hints so you can tune your transforms without heavy desktop tooling.
This site exists to give you:
- Release notes and product updates.
- Short guides that mirror common integration tasks.
- Deployment options, including a Helm chart you can point at your own cluster.
Everything is Markdown in `site/content/`, and the generated output lives in `docs/` so you can publish easily via GitHub Pages alongside the chart index.
+15
View File
@@ -0,0 +1,15 @@
---
title: "Welcome to the XSLT Playground blog"
description: "What you will find here and how it helps you get more from the online XSLT editor."
date: 2024-10-10T00:00:00Z
---
[XSLT Playground](https://xsltplayground.com) is a browser-first XSLT editor that lets you run transforms with multiple inputs and parameters, see results instantly, and spot performance hotspots without installing anything. This blog gathers the practical pieces you need around it.
What you will find here:
- Product updates and release notes for the playground.
- XSLT debugging recipes tailored to multi-parameter, real-world scenarios.
- Deployment notes, including the Helm chart and self-hosting guidance.
If you want to try it now, head straight to [xsltplayground.com](https://xsltplayground.com). If you want to contribute content, edit the Markdown in `site/content/` and rebuild the site with `hugo --source site --minify` to publish to `docs/`.
@@ -0,0 +1,47 @@
---
title: "Why I built an XSLT online editor for real-world work"
description: "The story behind XSLT Playground—an XSLT online editor tuned for multi-parameter transforms, fast feedback, and practical debugging."
date: 2024-10-20T00:00:00Z
---
If you have spent years inside XPath and XSLT, you know the pain of juggling heavy desktop tools just to ship one transform. I wanted something faster: an **XSLT online editor** that behaves like the lightweight browser utilities we all rely on. That is why I created [XSLT Playground](https://xsltplayground.com), a web-based editor built for realistic, multi-input scenarios instead of the single-input demos most tools target.
## The gap traditional editors left open
- In the 2000s, XML Spy was the default XML IDE, but XSLT editing and debugging always felt bolted on.
- Oracle JDeveloper and later Altova tools like MapForce improved visualization and isolated execution, yet they remained heavy, licensed, and resource-hungry.
- Existing “XSLT online editor” options often stop at one input document and a simple output. Real integrations rarely look that tidy.
## Inspired by the best small web utilities
For quick daily tasks, I still open [regex101](https://regex101.com) to debug patterns, [base64decode.org](https://www.base64decode.org/) to inspect payloads, and [epochconverter.com](https://www.epochconverter.com/) for timestamps. They are fast, focused, and always there. XSLT deserved the same “open browser, get answers” experience.
## What I actually needed in an online XSLT editor
- **Multiple parameters and sources**: real transformations combine several XML fragments, parameters, and metadata—not just one tidy input.
- **Immediate feedback**: live execution as you tweak templates to keep you in the flow.
- **Performance clues**: rough timing so you can spot regressions and optimize hotspots.
- **Zero install**: works in the browser without heavyweight downloads.
## What XSLT Playground delivers
- Multi-input, parameter-rich execution that mirrors enterprise and integration workloads.
- Live runs and inline results so you can iterate without context switching.
- Lightweight UX that feels like other playgrounds (think [jqplay.org](https://jqplay.org)) but purpose-built for XSLT.
- Direct access at [xsltplayground.com](https://xsltplayground.com) whenever you need an **online XSLT editor**.
## Who should try it
- Engineers integrating XML across systems who need a reliable **XSLT online editor** for quick debugging.
- Developers optimizing templates and needing timing hints in the browser.
- Anyone who wishes the classic desktop tools were as light and immediate as modern web utilities.
## Executive Summary
- Tool: [XSLT Playground](https://xsltplayground.com) — browser-based **XSLT online editor** with multi-parameter inputs, live output, and timing.
- Problem: legacy editors are heavy, single-input, and slow to iterate.
- Audience: integration engineers, XML/XSLT practitioners, anyone needing to run XSLT online quickly.
- Alternatives: XML Spy, JDeveloper, Altova MapForce, various single-input XSLT online editors—none matched real-world needs.
- Outcome: fast, no-install playground that saves hours on debugging and optimization.
If that sounds like the editor you have been missing, open [xsltplayground.com](https://xsltplayground.com) and run your next transform right now.
+97
View File
@@ -0,0 +1,97 @@
---
title: "XSLT 3.0 new features: what changed from 2.0"
description: "A practical guide to XSLT 3.0 features including streaming, maps, arrays, and JSON support. What to adopt and why."
date: 2025-02-10T00:00:00Z
tags: ["xslt3", "saxon", "xslt2"]
---
XSLT 3.0 is a significant step beyond 2.0. If you are running Saxon on the backend — as XSLT Playground does — you have access to the full 3.0 feature set today. This post covers the additions that matter most in practice and shows how to try each one directly in the playground.
## Streaming
The most impactful change in 3.0 is streaming. In earlier versions, the processor loads the entire source document into memory before any template can run. With 3.0 streaming, selected templates can consume the document as a stream, which drastically reduces memory usage for large inputs.
To enable streaming, declare it on the stylesheet and on the mode:
```xml
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:mode streamable="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
```
Not every expression is streamable. Saxon will tell you at compile time if a pattern is not compatible. The key restriction is that you can only visit each node once — no backward axes, no variables that hold nodes for later inspection.
## Maps and arrays
XSLT 3.0 adds maps and arrays as first-class values, borrowed from XPath 3.1. A map is a collection of key-value pairs; an array is an ordered sequence that can hold any value including other maps or arrays.
```xml
<xsl:variable name="config" select="map{
'currency': 'EUR',
'precision': 2,
'separator': ','
}"/>
<xsl:value-of select="$config('currency')"/>
```
Arrays work similarly:
```xml
<xsl:variable name="codes" select="['US', 'GB', 'DE']"/>
<xsl:value-of select="$codes(1)"/>
```
Arrays use 1-based indexing. Use `array:size()`, `array:get()`, and `array:append()` from the array namespace for common operations.
## JSON input and output
XSLT 3.0 can parse and produce JSON natively via `json-to-xml()` and `xml-to-json()`. This eliminates the need for a pre-processing step when your source is JSON.
```xml
<xsl:variable name="parsed" select="json-to-xml($json-string)"/>
```
The function converts JSON into a predictable XML representation defined by the W3C. Objects become `<map>` elements, arrays become `<array>`, and primitives become typed `<string>`, `<number>`, or `<boolean>` elements. You transform this intermediate XML normally and then serialize it back with `xml-to-json()` if needed.
## Higher-order functions
You can now pass functions as arguments using `xsl:function` and the `function()` type. This enables patterns like map, filter, and fold over sequences without recursion.
```xml
<xsl:function name="my:double" as="xs:integer">
<xsl:param name="n" as="xs:integer"/>
<xsl:sequence select="$n * 2"/>
</xsl:function>
<xsl:variable name="doubled" select="for-each((1,2,3,4), my:double#1)"/>
```
The `#1` notation creates a function reference with arity 1.
## Packages
XSLT 3.0 introduces packages, which let you split a large stylesheet into independently compiled units that expose explicit interfaces. This is the equivalent of modules or libraries in other languages.
```xml
<xsl:package name="http://example.com/utils" version="1.0">
<xsl:expose component="function" names="my:format-date" visibility="public"/>
...
</xsl:package>
```
Packages reduce coupling and enable reuse across projects without copy-paste.
## Try it in XSLT Playground
All the examples above run in [XSLT Playground](https://xsltplayground.com) with version set to 3.0. Maps and JSON support are the quickest to explore. Paste the `json-to-xml()` example, provide a JSON string as input, and see the intermediate representation immediately.
XSLT 3.0 is available today. If your integration still targets 2.0, the features above are the best reasons to upgrade.
@@ -0,0 +1,33 @@
---
title: "XSLT debugging patterns that save hours"
description: "Practical ways to trace, isolate, and fix transformations with minimal friction."
date: 2024-11-18T00:00:00Z
---
XSLT bugs are rarely loud. More often, a template silently matches the wrong node, a predicate filters out a value you needed, or a namespace mismatch turns an element into a ghost. The fastest fix comes from a repeatable debugging workflow that keeps your assumptions visible. Over time you learn the same patterns appear in almost every real project, whether you are cleansing XML feeds, integrating partner payloads, or generating documents. This post walks through the techniques I use as an integration engineer to debug transforms quickly without losing context.
Start by making the matching rules obvious. The majority of issues are caused by using `//` too freely or relying on default namespaces. Replace broad paths with anchored ones, and when in doubt, print out what the processor thinks the current node is. A simple `xsl:message` combined with `name()` and `namespace-uri()` can reveal a namespace mismatch in seconds. I also add short, temporary templates that match the suspected nodes and output minimal text, which is a fast way to confirm whether the selection is correct.
Next, isolate the failing region by reducing input size. You rarely need the entire input document to debug a single mapping. Extract the smallest fragment that reproduces the issue and run the transform against that. This lets you simplify predicates and remove unrelated templates. When a transform uses `xsl:key`, add a temporary output that lists the key index for a given value so you can see if the key is being built correctly. The same idea works for variables: output them just once in a deterministic area of the result so you can verify their shape.
A stable debug transform also benefits from deterministic ordering. When you iterate through nodes, add an explicit `xsl:sort` so the output is predictable. That makes diffs meaningful when you tweak a predicate or update a template priority. If you are mixing modes, ensure the call chain is explicit; a missing `mode` is a classic way to call a generic template by accident. A related trap is having a high-priority identity template that overrides a specialized one, so watch for priority values and make sure the most specific template wins.
When handling multiple inputs, be clear about document boundaries. Use `document()` or `collection()` with explicit base URIs and add messages that show which document node you are iterating. If you are using XSLT 2.0 or 3.0, a quick `serialize()` to a short string can show you whether the tree is what you expect. If you stick to XSLT 1.0, the same idea works by writing `xsl:copy-of` into a separate debug result tree and inspecting it.
Here is a short pattern I often add while troubleshooting:
```xml
<xsl:template match="*">
<xsl:message>
node=<xsl:value-of select="name()"/>
ns=<xsl:value-of select="namespace-uri()"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
```
You can drop this at the top of the stylesheet, run a quick transform, and then remove it once the root cause is found. The idea is not to keep noise in production, but to have a fast way to make the invisible visible. For more focused tracing, add the template only for the nodes you suspect are wrong. Debugging gets faster the more you scope down the noise.
Finally, keep a checklist of the classic XSLT footguns: missing namespaces, wrong context node, incorrect `@` in attribute selection, and predicates that use 1-based indexes when you thought they were 0-based. I also look for template import precedence issues and for unexpected whitespace handling when the output is textual. These are easy to miss because the transform still runs, it just runs incorrectly.
If you want a fast place to test these patterns with real inputs, use the online editor at [https://xsltplayground.com](https://xsltplayground.com). It is built for rapid iteration with multiple inputs and parameters, which makes debugging much less painful and keeps your feedback loop tight.
+131
View File
@@ -0,0 +1,131 @@
---
title: "XSLT for beginners: your first transformation"
description: "Learn XSLT from scratch with practical examples. Transform XML step by step using templates, match patterns, and value-of."
date: 2025-02-20T00:00:00Z
tags: ["beginners", "xslt", "tutorial"]
---
XSLT is a language for transforming XML documents into other formats — another XML structure, HTML, plain text, or JSON. If you are new to it, the learning curve can feel steep because XSLT is declarative and template-driven, which is different from procedural languages. This guide walks you through the core ideas with working examples you can run in [XSLT Playground](https://xsltplayground.com) right now.
## What XSLT does
You start with an XML source document. You write a stylesheet that describes rules for transforming it. The processor reads both and produces an output document. The stylesheet does not loop through the input line by line — instead, it defines templates that match nodes, and the processor calls those templates as it traverses the document tree.
## Your first stylesheet
Start with a simple XML document:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<book id="b1">
<title>Design Patterns</title>
<author>Gang of Four</author>
<price>45.00</price>
</book>
<book id="b2">
<title>Clean Code</title>
<author>Robert Martin</author>
<price>38.00</price>
</book>
</catalog>
```
Now write a stylesheet that turns this into an HTML table:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<table border="1">
<tr>
<th>Title</th>
<th>Author</th>
<th>Price</th>
</tr>
<xsl:apply-templates select="catalog/book"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="book">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="author"/></td>
<td><xsl:value-of select="price"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
```
Paste both into [XSLT Playground](https://xsltplayground.com) and run it. You will see an HTML table with the book data.
## Understanding templates
The `<xsl:template match="/">` rule fires when the processor reaches the document root. Inside it, `<xsl:apply-templates select="catalog/book"/>` tells the processor to find all `book` elements inside `catalog` and call the matching template for each one.
The second template `<xsl:template match="book">` fires once per `book` element. Inside it, `<xsl:value-of select="title"/>` extracts the text content of the `title` child.
This is the core loop of XSLT: match, apply, select.
## XPath selects nodes
The `select` and `match` attributes use XPath, a path language for navigating XML trees. A few rules you will use constantly:
| XPath | Meaning |
|---|---|
| `catalog/book` | `book` children of `catalog` |
| `//book` | All `book` elements anywhere in the document |
| `book/@id` | The `id` attribute of `book` |
| `book[price > 40]` | Books whose price exceeds 40 |
| `.` | The current node |
| `..` | The parent of the current node |
## Filtering with predicates
Add a predicate to the `apply-templates` call to show only books over 40:
```xml
<xsl:apply-templates select="catalog/book[price > 40]"/>
```
Or use `xsl:if` inside the template:
```xml
<xsl:template match="book">
<xsl:if test="price > 40">
<tr>
<td><xsl:value-of select="title"/></td>
</tr>
</xsl:if>
</xsl:template>
```
## Sorting output
Use `xsl:sort` to control the order:
```xml
<xsl:apply-templates select="catalog/book">
<xsl:sort select="price" data-type="number" order="ascending"/>
</xsl:apply-templates>
```
## What to try next
Once you are comfortable with templates and XPath:
- Learn `xsl:for-each` for inline iteration
- Explore `xsl:choose` for multi-branch conditionals
- Try `xsl:variable` to store intermediate values
- Look at `xsl:param` to pass values into your stylesheet from outside
All of these work in XSLT 1.0, 2.0, and 3.0. The [XSLT Playground](https://xsltplayground.com) lets you set the version and experiment without installing anything.
@@ -0,0 +1,25 @@
---
title: "Architecting XSLT in integration pipelines"
description: "Where XSLT fits in modern systems and how to keep transforms clean."
date: 2024-11-28T00:00:00Z
---
XSLT is at its best when it is treated as a focused transformation component in a wider integration architecture. You can use it inside an ESB, as part of a serverless function, or embedded in a data processing pipeline. The key is to define its responsibility clearly: XSLT should transform shape and content, not contain hidden transport logic or business workflows. With that boundary in place, the stylesheet becomes easier to test, reuse, and evolve.
A good architecture separates input acquisition, transformation, and delivery. The system that receives the payload should normalize and validate it before passing it to the XSLT step. After the transform, another component handles delivery or storage. This keeps the stylesheet focused and reduces the risk of it failing because of environment assumptions. It also makes it easier to swap processors or upgrade XSLT versions without touching unrelated logic.
When integrating multiple sources, think of XSLT as a join and normalization engine. The stylesheet can combine a transaction payload with reference data, language resources, or configuration parameters. The calling system should provide those inputs explicitly rather than having the stylesheet reach out to remote systems. This makes the transform deterministic and reduces operational risk. It also improves security because the transform does not need to access external networks.
Versioning is another important part of integration architecture. Treat stylesheets like code and version them alongside the application that uses them. Include an identifier in the stylesheet that indicates its version and expected input schema. If you have multiple partner versions, consider a dispatch layer that selects the correct stylesheet based on input metadata. This avoids cluttering a single stylesheet with dozens of conditionals and makes maintenance much easier.
Pay attention to error handling. XSLT can fail when the input is malformed or missing required elements. Decide whether the transform should fail fast or produce a partial output with warnings. In critical integrations, I prefer to fail fast and let the pipeline route the message to an error queue. For non-critical outputs, you may choose to emit defaults and collect warnings. Either way, keep the error strategy consistent and visible in the stylesheet.
Performance and scalability often depend on your processor configuration. If you run large transforms, use a streaming-capable processor where possible, and avoid functions that require the whole tree if you do not need it. In XSLT 3.0, streaming modes can reduce memory pressure significantly. If you are still on XSLT 1.0, the best strategy is to keep documents small and avoid deep scans.
Documentation matters more than you think. A short diagram or README that shows inputs, parameters, and outputs can save days of debugging. Include a section that lists required inputs and optional ones, and document the default values for parameters. This makes it easy for new team members to use the stylesheet correctly and reduces integration errors.
Security is another architectural concern. Treat external payloads as untrusted and validate them before the transform. Limit external entity expansion and disable features you do not need in the processor configuration. If you pass data between services, make sure you are not leaking sensitive fields in the output, and keep a clear mapping of which fields are retained or dropped.
The final piece is tooling. You need a place to test, debug, and share transforms with the team. A browser-based editor is ideal for quick iteration, especially when you are coordinating across teams or validating partner payloads. It lowers the barrier to entry and speeds up the feedback loop during integration work.
If you want a reliable place to iterate on transforms with multiple inputs and parameters, try the online editor at [https://xsltplayground.com](https://xsltplayground.com). It is a practical tool for integration engineers who need fast feedback without complex setup.
@@ -0,0 +1,32 @@
---
title: "Designing XSLT transforms with parameters and multiple inputs"
description: "How to structure stylesheets that consume several XML documents and stay maintainable."
date: 2024-11-20T00:00:00Z
---
Many real-world transformations do not run on a single XML document. You often merge a primary payload with reference data, catalog lookups, or environment configuration. Done well, this results in a clean, predictable transform. Done poorly, it becomes a maze of `document()` calls and hidden dependencies. The difference is in how you model inputs and parameters from the start. As an integration engineer, I treat input selection and parameter design as first-class API design for the stylesheet.
Start by naming every input. Instead of embedding `document('config.xml')` in multiple templates, load each external document once near the top of the stylesheet and bind it to a global variable. This makes dependencies explicit and keeps the rest of the code focused on mapping. It also helps with testing, because you can override the URI with a parameter. A clean pattern is to define `xsl:param` values for input URIs and then bind them to `xsl:variable` values that hold the parsed documents.
The same clarity applies to parameters. Keep parameters primitive and predictable, and avoid passing in node sets unless you truly need them. A parameter should be an external knob: region, language, a feature flag, or an output format. If you have a complex decision tree, consider using a lookup XML or JSON input and then query it inside the stylesheet. This approach keeps the invocation interface stable while still letting you evolve business rules.
A simple skeleton might look like this:
```xml
<xsl:param name="catalog-uri"/>
<xsl:param name="region" select="'us'"/>
<xsl:variable name="catalog" select="document($catalog-uri)"/>
```
From there, templates can reference `$catalog` without worrying about IO or base URIs. You can also define a named template that accepts a parameter for reuse across multiple modes. This is useful when the same output block is needed for several sections of the document but the selection context differs.
When combining multiple inputs, always anchor your lookups to a clear key. If you can, define `xsl:key` on the external document so lookups are efficient and readable. In XSLT 2.0 or 3.0, `xsl:for-each-group` and the `map` types can reduce boilerplate, but the core idea remains: make your joins explicit and deterministic. If you rely on default order or on undocumented assumptions about uniqueness, you will eventually get a hard-to-reproduce bug.
Another important integration pattern is separating parsing from formatting. For example, you might normalize all values from the various inputs into a canonical intermediate structure and then render that structure into the final output. This makes testing easier and supports future outputs such as CSV, JSON, or a secondary XML format. Even in XSLT 1.0, you can emulate this by creating result tree fragments, then processing them in a second pass if needed.
Multiple inputs also raise questions about fallbacks. Decide how you want to behave when optional data is missing. I prefer to centralize defaults in a few named templates or functions and avoid sprinkling `xsl:choose` blocks everywhere. This keeps the stylesheet readable and makes it obvious how to override the defaults later. Document your fallbacks in the code with short, clear names so a future maintainer does not have to rediscover the rules by reading the entire stylesheet.
Finally, create a small set of inputs that represent common scenarios and run them regularly. For example, have a baseline case, a case with missing reference data, and a case with unexpected elements. These are the cases that reveal poor assumptions about inputs. A fast way to iterate on these scenarios is to run the transform with a tool that lets you swap inputs and parameters quickly.
If you want to try these patterns with real inputs and multiple documents, the online editor at [https://xsltplayground.com](https://xsltplayground.com) is built for that workflow. It lets you load multiple XML documents and parameters, see how they interact, and keep your integration logic transparent as it grows.
@@ -0,0 +1,25 @@
---
title: "XSLT performance tuning without losing readability"
description: "A practical guide to faster transformations with keys, modes, and smarter selection."
date: 2024-11-22T00:00:00Z
---
Performance problems in XSLT are sneaky. The stylesheet looks clean, the output is correct, but the transform slows down as the input grows. Most of the time this is caused by expensive selections that are repeated in loops, or by deep `//` searches that scan the entire tree more often than you expect. The good news is that you can usually fix these issues without turning the stylesheet into unreadable micro-optimizations.
The first step is to examine where you are traversing the document. XSLT processors are optimized for template matching, so prefer `xsl:apply-templates` and specific match patterns over `xsl:for-each` with `//` in the select. When you do need a search, limit it to the smallest possible subtree. A single `//` at the top-level becomes a full-tree scan each time it runs. If it runs inside another loop, the cost can explode.
Keys are the most important performance feature, and they also improve readability. When you define an `xsl:key`, you turn a repeated search into a fast lookup. This is especially critical for join-like operations where you match a reference value to another document or a secondary section of the same document. Build the key once, and then use `key('id', $value)` everywhere. The intent becomes clear: you are doing a lookup, not a scan. If you only use keys occasionally, it can feel like overkill, but it is often the biggest win.
Modes are another useful tool. If you use the same templates in multiple contexts, you may end up doing extra work or firing templates that you do not need. A dedicated mode lets you create a focused processing pipeline that touches only the nodes relevant to that output section. This can reduce both runtime and mental overhead. It also makes it easier to reason about precedence: within a mode, you can define more specific templates without worrying about side effects on unrelated parts of the transform.
Consider caching computed values in variables. XSLT variables are immutable, so they are safe to reuse without unintended side effects. If you are computing a complex string or a filtered node set repeatedly, store it once per relevant scope. Just be careful not to define a variable at the top of the stylesheet if it depends on the context; keep it as close as possible to where it is used to avoid confusion.
If you are working in XSLT 2.0 or 3.0, you gain access to `xsl:for-each-group` and higher-order functions. These can be faster and clearer than manual grouping with keys. For XSLT 1.0, the Muenchian grouping pattern is still effective, and when combined with keys it remains a strong choice. Either way, focus on minimizing passes over large node sets.
Also consider the output method. Serializing large outputs can be a significant part of the runtime. If you do not need pretty-printed XML, avoid indentation to reduce the amount of whitespace and processing time. Similarly, if you are generating text or JSON, use `method="text"` or structured XSLT 3.0 serialization options rather than building a text string node by node.
I recommend using realistic test data when tuning performance. A transform that runs in 100 milliseconds on a tiny input may take seconds on real data. Use a handful of real documents and measure changes as you apply each improvement. This keeps the optimization process grounded and prevents you from making the code worse without a measurable gain.
Finally, keep a balance between speed and clarity. The fastest stylesheet is useless if it is too hard to maintain. Use a few consistent patterns: keys for lookups, modes for pipelines, variables for repeated values, and limited selection scope. With those in place, the performance usually becomes acceptable without heroics.
If you want a quick way to benchmark different approaches with the same input set, try the online editor at [https://xsltplayground.com](https://xsltplayground.com). It is a convenient place to experiment with keys, modes, and alternative match patterns while keeping your transform readable.
@@ -0,0 +1,107 @@
---
title: "XSLT template matching explained with examples"
description: "How XSLT template matching works: priorities, conflict resolution, modes, and default templates. With practical examples."
date: 2025-03-05T00:00:00Z
tags: ["xslt", "templates", "xpath"]
---
Template matching is the mechanism that drives every XSLT transformation. Understanding how the processor selects templates — and what happens when multiple templates could match — is the difference between a stylesheet that works reliably and one that produces surprising output. This post covers everything you need to know.
## How match patterns work
When the processor visits a node, it evaluates every template's `match` attribute as an XPath pattern. A pattern is a restricted form of XPath that tests properties of a node rather than selecting nodes from a starting point. If the pattern is satisfied, that template is a candidate.
```xml
<xsl:template match="book"> <!-- matches any book element -->
<xsl:template match="catalog/book"> <!-- matches book that is a child of catalog -->
<xsl:template match="book[@id]"> <!-- matches book elements with an id attribute -->
<xsl:template match="*"> <!-- matches any element -->
<xsl:template match="text()"> <!-- matches any text node -->
<xsl:template match="@*"> <!-- matches any attribute -->
```
## Priority and conflict resolution
More than one template can match the same node. The processor resolves the conflict using priority. Each pattern has a default priority calculated by the spec:
| Pattern type | Default priority |
|---|---|
| `node()` or `*` | -0.5 |
| `element-name` | 0 |
| `prefix:element-name` | 0 |
| `a/b` (path) | 0.5 |
| `a[predicate]` | 0.5 |
| `@attr` | 0 |
More specific patterns automatically get higher priority. You can override this with the `priority` attribute:
```xml
<xsl:template match="book" priority="1">
<!-- this wins over a match="book" template at default priority -->
</xsl:template>
```
If two templates have equal computed priority, the processor signals an error (or picks the last one, depending on implementation — Saxon issues an error by default). Always assign explicit priorities when you have competing templates.
## The built-in templates
XSLT has default templates for every node type. If no explicit template matches a node, the built-in fires. For elements and the document root, the built-in calls `apply-templates` on all children. For text and attribute nodes, it outputs the string value.
This means that without any templates at all, the processor will walk the entire tree and output all text content. Understanding this explains why simple stylesheets can produce unexpected extra text — a text node matched nothing explicit, and the built-in output it.
To suppress text output globally, add:
```xml
<xsl:template match="text()"/>
```
This overrides the built-in with an empty template, producing no output for text nodes that are not handled elsewhere.
## Modes
Modes let you have multiple templates for the same node that serve different purposes. A mode is a named context for a set of templates.
```xml
<!-- Default mode: output a summary row -->
<xsl:template match="book">
<tr><td><xsl:value-of select="title"/></td></tr>
</xsl:template>
<!-- detail mode: output a full card -->
<xsl:template match="book" mode="detail">
<div class="card">
<h2><xsl:value-of select="title"/></h2>
<p><xsl:value-of select="description"/></p>
</div>
</xsl:template>
```
Call with mode:
```xml
<xsl:apply-templates select="catalog/book" mode="detail"/>
```
Modes are especially useful when you need to process the same nodes in multiple places in the output with different logic each time.
## apply-templates vs for-each
Both iterate over a set of nodes. The difference is that `apply-templates` dispatches to the best matching template for each node, while `for-each` stays in the current context and does not do template lookup.
Use `apply-templates` when you want polymorphism — different node types handled differently. Use `for-each` when you are doing a simple iteration over a homogeneous set and do not need dispatch.
```xml
<!-- apply-templates: each node can match a different template -->
<xsl:apply-templates select="items/*"/>
<!-- for-each: all items handled inline -->
<xsl:for-each select="items/item">
<li><xsl:value-of select="."/></li>
</xsl:for-each>
```
## Testing patterns in XSLT Playground
[XSLT Playground](https://xsltplayground.com) is a fast way to experiment with matching rules. Paste a stylesheet with multiple competing templates and check which one fires. Add `<xsl:message select="name()"/>` in each template to trace which one the processor picks. The trace panel shows all messages in order so you can follow the dispatch chain.
Solid understanding of template matching pays off every time you work on a complex stylesheet. Once you know how priorities and built-ins interact, most "unexpected output" bugs become obvious.
@@ -0,0 +1,25 @@
---
title: "Testing XSLT transforms for regression safety"
description: "How to build a lightweight test harness for reliable XSLT deployments."
date: 2024-11-26T00:00:00Z
---
XSLT transformations often live at the heart of an integration flow. A small change can impact downstream systems, and because the output is just data, regressions can go unnoticed until a business process breaks. You do not need a massive testing framework to prevent this. A lightweight, repeatable testing approach with a few representative inputs can catch most issues and make changes far safer to deploy.
Start by curating a compact test corpus. Choose a handful of XML inputs that represent the most important scenarios: a normal case, a case with missing optional elements, a case with unexpected or additional fields, and a case with edge values such as empty strings or special characters. Keep these documents small and focused so you can understand the expected output at a glance. Store them alongside the stylesheet to keep the transformation and tests tightly connected.
Next, define expected outputs. This can be literal expected output files, or it can be key assertions. In XSLT 2.0 and 3.0, you can use `xsl:assert` to enforce invariants such as required elements or output ordering. In XSLT 1.0, you can still build a test harness by comparing the output to known-good files. The important thing is that the tests are deterministic and easy to run.
A practical approach is to create a wrapper script that runs the transform against each input and diffs the output against the expected result. If you are using a CI pipeline, this is easy to automate. If you are running locally, you can use a simple shell script or a makefile target. The key is to reduce friction so you actually run the tests before shipping a change.
Beyond output equality, consider adding structure checks. For example, if your output is XML, use an XML-aware diff or validate the output against a schema. For JSON outputs, parse and validate. For CSV, load into a parser and verify column count. These checks help catch cases where output is syntactically valid but structurally wrong. They also help maintain consistent ordering when you refactor templates.
One effective pattern is to include a test-only mode in your stylesheet. In this mode, you can output debug traces or additional metadata that is useful for validation. You keep the production output clean while making it easier to assert internal behavior during tests. The mode can be controlled with a parameter, so you do not need a separate stylesheet.
If your transform depends on external inputs, mock them. For example, if you read reference data from a lookup file, keep a small version of that file in your test fixtures. This makes the tests independent and fast. The smaller and more deterministic the inputs, the easier it is to interpret test failures.
Finally, document the test scenarios. A short README with a one-line description of each fixture is enough. When new cases come in, add them to the test set and keep the suite small but representative. Over time, this becomes a powerful safety net that speeds up changes rather than slowing them down.
One more tip: keep track of transformation time for each fixture. Even a simple timestamp around the transform can reveal regressions that functional tests do not catch. If a mapping suddenly takes twice as long, that is a signal to review recent changes or input growth. Performance is part of correctness in integration flows, and small slowdowns can turn into outages when volume spikes.
If you want to iterate on tests and outputs quickly, the online editor at [https://xsltplayground.com](https://xsltplayground.com) is a convenient place to run your fixtures, compare outputs, and refine expectations before you bake them into your automated checks.
@@ -0,0 +1,33 @@
---
title: "Transforming XML to JSON and CSV with XSLT"
description: "Patterns for producing modern integration formats while staying in XSLT."
date: 2024-11-24T00:00:00Z
---
XSLT is usually associated with XML-to-XML transformations, but in integration work you often need JSON or CSV. The good news is that XSLT is perfectly capable of producing non-XML outputs when you design the stylesheet for it. The key is to choose the right output method, control whitespace carefully, and build an intermediate structure if it helps clarify the mapping. This post covers practical patterns for generating JSON and CSV from XML while keeping the stylesheet maintainable.
For JSON, the simplest method is to output text and build the JSON structure manually. This gives you precise control, but it also requires careful escaping and formatting. If you are on XSLT 3.0, use maps and arrays and let the processor serialize to JSON. This reduces string manipulation and makes your transform more robust. If you are on XSLT 1.0 or 2.0, you can still build JSON text safely by using templates that escape quotes, backslashes, and control characters.
A clear pattern is to create a template that takes a string and outputs an escaped JSON string. Then, for each object, output the property names and values with explicit commas. Keep a template to handle comma placement so you do not end up with trailing commas in arrays. This is a good place to use position checks like `position() != last()` to decide when to emit a comma. While it can look verbose, the logic is deterministic and easy to debug.
CSV output is simpler but comes with its own hazards. You need to wrap fields that contain commas, quotes, or line breaks. The common rule is to wrap the field in quotes and double any interior quotes. Again, a dedicated template to escape fields pays off. Define the column order explicitly and avoid depending on the source document order. This keeps the CSV consistent even if the XML input changes slightly. If you need multiple CSV sections, consider running two passes: one to compute the rows and one to serialize them.
An example CSV field template can look like this in XSLT 1.0:
```xml
<xsl:template name="csv-field">
<xsl:param name="value"/>
<xsl:variable name="escaped" select="translate($value, '&quot;', '&quot;&quot;')"/>
<xsl:text>"</xsl:text>
<xsl:value-of select="$escaped"/>
<xsl:text>"</xsl:text>
</xsl:template>
```
This gives you a reusable building block and keeps the main row template readable. You can also pair it with a `csv-row` template that inserts commas between fields. The result is a clear structure where you can change column order without touching the escaping logic.
When moving between XML and JSON/CSV, consider creating a normalized intermediate structure. For example, if the input XML has a deep hierarchy but your output is a flat list, create a lightweight node set representing rows and columns first. Then serialize that representation into your target format. This approach makes the mapping more explicit and keeps string-heavy output logic confined to a small section of the stylesheet.
Testing is crucial because formatting errors are easy to miss. Validate JSON output with a JSON parser and load CSV into a spreadsheet or a small parser to confirm columns align. This is also where you will notice if a newline or a stray comma slipped in. To keep iteration fast, run your transform with a tool that allows quick input swaps and immediate output inspection.
If you want a quick way to experiment with JSON or CSV output, the online editor at [https://xsltplayground.com](https://xsltplayground.com) is a great option. It lets you run transforms with multiple inputs and see the serialized output instantly, which makes it easy to refine your JSON and CSV strategies.
+42
View File
@@ -0,0 +1,42 @@
baseURL = "https://blog.xsltplayground.com/"
languageCode = "en-us"
title = "XSLT Playground"
publishDir = "../docs"
enableRobotsTXT = true
[pagination]
pagerSize = 10
[params]
subtitle = "Online XSLT editor built for real-world transformations."
lead = "Run and debug XSLT online with multi-parameter inputs, timing hints, and fast feedback. This site hosts release notes, guides, and deployment options for XSLT Playground."
[params.adsense]
# Prefer setting ADSENSE_CLIENT and ADSENSE_INARTICLE_SLOT env vars when building if you don't want the values committed.
client = "ca-pub-1549720748100858"
inArticleSlot = ""
[params.ethicalads]
publisher = "xsltplaygroundcom"
[menu]
[[menu.main]]
name = "Home"
url = "/"
weight = 1
[[menu.main]]
name = "Posts"
url = "/posts/"
weight = 2
[[menu.main]]
name = "About"
url = "/about/"
weight = 3
[[menu.main]]
name = "Charts"
url = "https://alexandrev.github.io/xslt-lab/index.yaml"
weight = 4
+39
View File
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ block "title" . }}{{ with .Title }}{{ . }} | {{ end }}{{ .Site.Title }}{{ end }}</title>
<meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.subtitle }}{{ end }}">
{{ partial "adsense/head.html" . }}
<link rel="stylesheet" href="{{ "css/main.css" | relURL }}">
</head>
{{- $ethicalAds := and .Site.Params.ethicalads.publisher (ne hugo.Environment "development") -}}
<body{{ if $ethicalAds }} class="has-ethicalads"{{ end }}>
{{ partial "ethicalads.html" . }}
<header class="site-header">
<div class="container header-grid">
<div>
<a class="brand" href="{{ "/" | relURL }}">{{ .Site.Title }}</a>
<p class="tagline">{{ .Site.Params.subtitle }}</p>
</div>
<nav>
<ul class="nav">
{{ range .Site.Menus.main }}
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
{{ end }}
</ul>
</nav>
</div>
</header>
<main class="container">
{{ block "main" . }}{{ end }}
</main>
<footer class="site-footer">
<div class="container footer-grid">
<p>Built with Hugo · {{ now.Format "2006-01-02" }}</p>
<p class="muted">Repo: <a href="https://github.com/alexandrev/xslt-lab">alexandrev/xslt-lab</a></p>
</div>
</footer>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
{{ define "main" }}
<section class="page-header">
<p class="eyebrow">{{ .Section | title }}</p>
<h1>{{ .Title }}</h1>
{{ with .Description }}<p class="lead">{{ . }}</p>{{ end }}
</section>
<ul class="post-list spaced">
{{ range .Paginator.Pages }}
<li>
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
<span class="muted">{{ .Date.Format "02 Jan 2006" }}</span>
{{ with .Params.description }}<p class="muted">{{ . }}</p>{{ end }}
</li>
{{ end }}
</ul>
{{ template "_internal/pagination.html" . }}
{{ end }}
+13
View File
@@ -0,0 +1,13 @@
{{ define "main" }}
<article class="post">
<p class="eyebrow">{{ .Section | title }}</p>
<h1>{{ .Title }}</h1>
<p class="muted">{{ .Date.Format "02 Jan 2006" }}</p>
{{ with .Params.description }}<p class="lead">{{ . }}</p>{{ end }}
{{ partial "adsense/in-article.html" . }}
<div class="content">
{{ .Content }}
</div>
<p class="muted back-link"><a href="{{ .CurrentSection.RelPermalink }}">← Back to {{ .CurrentSection.Title }}</a></p>
</article>
{{ end }}
+42
View File
@@ -0,0 +1,42 @@
{{ define "main" }}
<section class="hero">
<p class="eyebrow">Online XSLT Editor</p>
<h1>{{ .Title }}</h1>
<p class="lead">{{ .Params.description | default .Site.Params.lead }}</p>
<div class="cta-row">
<a class="button" href="https://xsltplayground.com">Open the playground</a>
<a class="button ghost" href="{{ "/posts/" | relURL }}">Read updates</a>
<a class="button ghost" href="https://alexandrev.github.io/xslt-lab/index.yaml">Helm chart index</a>
</div>
</section>
<section class="grid">
<article class="card">
<p class="eyebrow">How the playground helps</p>
<h2>Built for real XSLT work</h2>
<p>Run XSLT online with multiple inputs and parameters, see results as you type, and get timing hints to spot regressions before they hit production.</p>
<ul class="checks">
<li>Multi-parameter transforms</li>
<li>Live execution and inline output</li>
<li>Timing to guide optimization</li>
</ul>
</article>
<article class="card">
<p class="eyebrow">Latest posts</p>
<h2>Recent updates</h2>
<ul class="post-list">
{{ $posts := first 3 (where .Site.RegularPages "Section" "posts") }}
{{ if gt (len $posts) 0 }}
{{ range $posts }}
<li>
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
<span class="muted">{{ .Date.Format "02 Jan 2006" }}</span>
</li>
{{ end }}
{{ else }}
<li class="muted">No posts yet. Add more in <code>site/content/posts/</code>.</li>
{{ end }}
</ul>
</article>
</section>
{{ end }}
@@ -0,0 +1,5 @@
{{- $client := .Site.Params.adsense.client -}}
{{- if and $client (ne hugo.Environment "development") -}}
<meta name="google-adsense-account" content="{{ $client }}">
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client={{ $client }}" crossorigin="anonymous"></script>
{{- end -}}
@@ -0,0 +1,15 @@
{{- $client := .Site.Params.adsense.client -}}
{{- $slot := .Site.Params.adsense.inArticleSlot -}}
{{- if and $client $slot (ne hugo.Environment "development") -}}
<div class="ad ad-in-article">
<ins class="adsbygoogle"
style="display:block; text-align:center;"
data-ad-layout="in-article"
data-ad-format="fluid"
data-ad-client="{{ $client }}"
data-ad-slot="{{ $slot }}"></ins>
</div>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
{{- end -}}
+13
View File
@@ -0,0 +1,13 @@
{{- $publisher := .Site.Params.ethicalads.publisher -}}
{{- if and $publisher (ne hugo.Environment "development") -}}
{{ if not (.Scratch.Get "ethicalads_loaded") -}}
<script async src="https://media.ethicalads.io/media/client/ethicalads.min.js"></script>
{{ .Scratch.Set "ethicalads_loaded" true }}
{{- end }}
<div class="ad-bar">
<div class="container ad-bar-inner">
<span class="muted">Sponsored</span>
<div class="ethical-ad" data-ea-publisher="{{ $publisher }}" data-ea-type="image"></div>
</div>
</div>
{{- end -}}
+1
View File
@@ -0,0 +1 @@
blog.xsltplayground.com
+261
View File
@@ -0,0 +1,261 @@
:root {
--bg: #0b1021;
--panel: #0f172a;
--accent: #32d5ff;
--accent-2: #ff7e67;
--text: #e6edf3;
--muted: #9fb2c8;
--border: #1f2a3d;
--shadow: 0 20px 40px rgba(0, 0, 0, 0.35);
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: radial-gradient(circle at 20% 20%, #11223f, #0b1021 45%), radial-gradient(circle at 80% 0%, #182743, #0b1021 40%), var(--bg);
color: var(--text);
line-height: 1.6;
--header-offset: 0px;
}
body.has-ethicalads {
--header-offset: 64px;
}
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-2); }
.container {
width: min(1100px, 90%);
margin: 0 auto;
}
.ad-bar {
position: sticky;
top: 0;
z-index: 20;
background: rgba(11, 16, 33, 0.95);
border-bottom: 1px solid var(--border);
min-height: 64px;
display: flex;
align-items: center;
}
.ad-bar-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.site-header {
border-bottom: 1px solid var(--border);
position: sticky;
top: var(--header-offset);
backdrop-filter: blur(8px);
background: rgba(11, 16, 33, 0.9);
z-index: 10;
}
.header-grid {
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
padding: 18px 0;
gap: 20px;
}
.brand {
font-weight: 800;
letter-spacing: -0.02em;
color: white;
}
.tagline {
margin: 4px 0 0;
color: var(--muted);
}
.nav {
display: flex;
gap: 14px;
list-style: none;
padding: 0;
margin: 0;
}
.nav a {
padding: 8px 12px;
border-radius: 10px;
border: 1px solid transparent;
}
.nav a:hover {
border-color: var(--border);
background: rgba(255, 255, 255, 0.04);
}
.hero {
padding: 70px 0 50px;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.2em;
font-size: 12px;
color: var(--muted);
margin: 0 0 10px;
}
h1, h2, h3, h4 {
margin: 10px 0;
line-height: 1.2;
}
.lead {
font-size: 18px;
color: #d7e4f4;
max-width: 780px;
}
.cta-row {
display: flex;
gap: 12px;
margin-top: 24px;
flex-wrap: wrap;
}
.button {
display: inline-block;
padding: 12px 18px;
border-radius: 12px;
background: linear-gradient(120deg, var(--accent), var(--accent-2));
color: #0b1021;
font-weight: 700;
box-shadow: var(--shadow);
}
.button.ghost {
background: transparent;
color: var(--text);
border: 1px solid var(--border);
box-shadow: none;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 24px;
padding: 10px 0 50px;
}
.card {
background: linear-gradient(145deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.01));
border: 1px solid var(--border);
border-radius: 16px;
padding: 18px 18px 22px;
box-shadow: var(--shadow);
}
.checks {
list-style: none;
padding: 0;
margin: 14px 0 0;
}
.checks li::before {
content: "✓";
color: var(--accent);
margin-right: 8px;
}
.post-list {
list-style: none;
padding: 0;
margin: 0;
}
.post-list li {
margin-bottom: 12px;
display: flex;
justify-content: space-between;
gap: 12px;
}
.post-list.spaced li {
padding: 14px 0;
border-bottom: 1px solid var(--border);
}
.muted {
color: var(--muted);
font-size: 14px;
}
.page-header {
padding: 40px 0 10px;
}
.post {
padding: 40px 0;
}
.post .content {
margin-top: 24px;
}
.ad {
margin: 26px 0;
padding: 14px 0;
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
}
.ad-in-article {
text-align: center;
}
.post .content p {
margin: 14px 0;
}
.post .content code {
background: rgba(255, 255, 255, 0.04);
padding: 2px 6px;
border-radius: 6px;
}
.back-link {
margin-top: 32px;
}
.site-footer {
border-top: 1px solid var(--border);
padding: 20px 0;
margin-top: 40px;
color: var(--muted);
}
.footer-grid {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
code {
font-family: "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 14px;
background: rgba(255, 255, 255, 0.06);
padding: 2px 5px;
border-radius: 6px;
}
@media (max-width: 720px) {
.header-grid { grid-template-columns: 1fr; }
.nav { flex-wrap: wrap; }
.post-list li { flex-direction: column; align-items: flex-start; }
}
+34
View File
@@ -0,0 +1,34 @@
# XSLT Playground
> XSLT Playground (xsltplayground.com) is a free online XSLT editor and tester that runs XSLT 1.0, 2.0, and 3.0 transformations using the Saxon processor. No installation required.
## What it does
- Run and test XSLT stylesheets directly in the browser
- Supports XSLT 1.0, 2.0, and 3.0 (Saxon-powered backend)
- Multiple XML inputs and parameters per transformation
- Trace output to inspect execution step by step
- Up to 3 independent workspaces, each with its own XSLT, XML input, parameters, and result
- Export and import workspaces as JSON to share setups
## Who uses it
Integration engineers, developers working with enterprise middleware (SAP, MuleSoft, Tibco, IBM), XML data engineers, and anyone who needs to write or debug XSLT without a heavy desktop IDE.
## Custom Saxon extensions
The backend exposes custom extension functions under `xmlns:tib="http://www.tibco.com/bw/xslt/custom-functions"`, including:
- `tib:uuid()` — generate a UUID
- `tib:timestamp()` — current timestamp
- `tib:addToDate()` — date arithmetic
- `tib:base64encode()` / `tib:base64decode()`
## Blog
Tutorials, guides, and release notes at blog.xsltplayground.com. Topics include XSLT 3.0 features, debugging patterns, integration architecture, performance tuning, and XML-to-JSON transformations.
## Links
- Tool: https://xsltplayground.com
- Blog: https://blog.xsltplayground.com
- Source: https://github.com/alexandrev/xslt-lab