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

SEO: fix internal linking, de-cannibalize, move blog to /blog/ subpath

Phase 1 — internal linking:
- Real [related] index (tags/keywords/category) in hugo.toml; rewrite
  related-posts.html to use .Related instead of "first 4 posts", which
  had left 12/20 posts with zero inbound links.
- Add tags to 6 previously untagged posts.

Phase 2 — de-cannibalization of the transactional cluster:
- Soft-301 mechanism (redirectTo -> meta refresh + noindex,follow) in baseof.
- xslt-online-editor-guide redirects to the app (pure duplicate of home).
- xslt-validator-online retargeted informational; drop the "common errors"
  section that cannibalized the errors hub, link to it instead.
- Migrate why-xslt-playground-online-editor into /about/ with an alias.
- Remove the duplicate <h1> on the app landing page.

Phase 3 — consolidate authority on the money domain:
- baseURL -> https://xsltplayground.com/blog/ (served via NPM reverse
  proxy to GitHub Pages; subdomain will 301 to the subpath).
- Rewrite internal blog links to /blog/... and cross-domain app links
  to xsltplayground.com/blog/...

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KZntQUfw463NW4ftgSjWw5
This commit is contained in:
2026-07-24 07:31:29 +00:00
parent 9bedbc3dc9
commit b2737ec715
23 changed files with 120 additions and 169 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# XSLT Playground — free online XSLT 1.0/2.0/3.0 tester & editor (Saxon HE)
**Live tool: [xsltplayground.com](https://xsltplayground.com)** · Blog: [blog.xsltplayground.com](https://blog.xsltplayground.com)
**Live tool: [xsltplayground.com](https://xsltplayground.com)** · Blog: [xsltplayground.com/blog](https://xsltplayground.com/blog)
XSLT Playground is a **free online XSLT editor, tester and validator** that runs XSLT
1.0, 2.0 and **3.0** on a real **Saxon HE 12.5** backend — the same processor used in
+2 -2
View File
@@ -291,7 +291,7 @@
<div class="shell-top">
<img src="/logo.svg" alt="XSLT Playground logo" class="shell-logo" fetchpriority="high" />
<div>
<h1 class="shell-title">Online XSLT Editor &amp; Tester</h1>
<p class="shell-title">Online XSLT Editor &amp; Tester</p>
<div class="shell-subtitle">Loading the editor...</div>
</div>
</div>
@@ -337,7 +337,7 @@
<div class="version-links">
<a href="/xslt-2-0/">XSLT 2.0 Online Tester</a>
<a href="/xslt-3-0/">XSLT 3.0 Online Tester</a>
<a href="https://blog.xsltplayground.com/xslt/functions/">XSLT &amp; XPath Function Reference</a>
<a href="https://xsltplayground.com/blog/xslt/functions/">XSLT &amp; XPath Function Reference</a>
</div>
<h2>Transform XML with XSLT Online — XSLT 1.0, 2.0 and 3.0</h2>
+2 -2
View File
@@ -25,10 +25,10 @@ The backend exposes custom extension functions under `xmlns:tib="http://www.tibc
## 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.
Tutorials, guides, and release notes at xsltplayground.com/blog. 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
- Blog: https://xsltplayground.com/blog
- Source: https://github.com/alexandrev/xslt-lab
+12 -12
View File
@@ -166,7 +166,7 @@
<a href="/">Editor</a>
<a href="/xslt-2-0/">XSLT 2.0</a>
<a href="/xslt-3-0/">XSLT 3.0</a>
<a href="https://blog.xsltplayground.com/">Blog</a>
<a href="https://xsltplayground.com/blog/">Blog</a>
</nav>
</header>
@@ -291,24 +291,24 @@
<h2>XSLT 2.0 Function Reference</h2>
<p>
Every XPath 2.0 function and XSLT 2.0 element, documented with syntax and a runnable example:
browse the <a href="https://blog.xsltplayground.com/xslt/functions/">complete XSLT &amp; XPath function reference</a>.
browse the <a href="https://xsltplayground.com/blog/xslt/functions/">complete XSLT &amp; XPath function reference</a>.
Popular XSLT 2.0 entries:
</p>
<ul>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xsl-for-each-group/">xsl:for-each-group</a> — grouping without Muenchian keys</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xpath-tokenize/">tokenize()</a> — split strings with regular expressions</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xpath-replace/">replace()</a> — regex search &amp; replace</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xpath-matches/">matches()</a> — test a string against a regex</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xpath-distinct-values/">distinct-values()</a> — deduplicate a sequence</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xsl-result-document/">xsl:result-document</a> — write multiple output files</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xsl-for-each-group/">xsl:for-each-group</a> — grouping without Muenchian keys</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xpath-tokenize/">tokenize()</a> — split strings with regular expressions</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xpath-replace/">replace()</a> — regex search &amp; replace</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xpath-matches/">matches()</a> — test a string against a regex</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xpath-distinct-values/">distinct-values()</a> — deduplicate a sequence</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xsl-result-document/">xsl:result-document</a> — write multiple output files</li>
</ul>
<h2>Learn More About XSLT 2.0</h2>
<ul>
<li><a href="https://blog.xsltplayground.com/posts/xslt-grouping-for-each-group/">XSLT grouping with xsl:for-each-group — complete guide</a></li>
<li><a href="https://blog.xsltplayground.com/posts/xslt-result-document/">Generating multiple output files with xsl:result-document</a></li>
<li><a href="https://blog.xsltplayground.com/posts/xslt-string-functions/">XSLT string functions reference (substring, replace, tokenize)</a></li>
<li><a href="https://blog.xsltplayground.com/posts/xslt-xml-to-json-csv/">Transforming XML to JSON and CSV with XSLT</a></li>
<li><a href="https://xsltplayground.com/blog/posts/xslt-grouping-for-each-group/">XSLT grouping with xsl:for-each-group — complete guide</a></li>
<li><a href="https://xsltplayground.com/blog/posts/xslt-result-document/">Generating multiple output files with xsl:result-document</a></li>
<li><a href="https://xsltplayground.com/blog/posts/xslt-string-functions/">XSLT string functions reference (substring, replace, tokenize)</a></li>
<li><a href="https://xsltplayground.com/blog/posts/xslt-xml-to-json-csv/">Transforming XML to JSON and CSV with XSLT</a></li>
</ul>
<h2>Also Available</h2>
+11 -11
View File
@@ -186,7 +186,7 @@
<a href="/">Editor</a>
<a href="/xslt-2-0/">XSLT 2.0</a>
<a href="/xslt-3-0/">XSLT 3.0</a>
<a href="https://blog.xsltplayground.com/">Blog</a>
<a href="https://xsltplayground.com/blog/">Blog</a>
</nav>
</header>
@@ -315,23 +315,23 @@
<h2>XSLT 3.0 Function Reference</h2>
<p>
Every XPath 3.1 function and XSLT 3.0 element, documented with syntax and a runnable example:
browse the <a href="https://blog.xsltplayground.com/xslt/functions/">complete XSLT &amp; XPath function reference</a>.
browse the <a href="https://xsltplayground.com/blog/xslt/functions/">complete XSLT &amp; XPath function reference</a>.
Popular XSLT 3.0 entries:
</p>
<ul>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xpath-map-merge/">map:merge()</a> — build and combine maps</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xpath-xml-to-json/">xml-to-json()</a> — convert XML to JSON output</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xpath-json-to-xml/">json-to-xml()</a> — parse JSON into XML</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xsl-iterate/">xsl:iterate</a> — streaming-friendly loops with state</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xsl-accumulator/">xsl:accumulator</a> — running totals while streaming</li>
<li><a href="https://blog.xsltplayground.com/xslt/functions/xpath-for-each/">for-each()</a> — higher-order functions over sequences</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xpath-map-merge/">map:merge()</a> — build and combine maps</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xpath-xml-to-json/">xml-to-json()</a> — convert XML to JSON output</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xpath-json-to-xml/">json-to-xml()</a> — parse JSON into XML</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xsl-iterate/">xsl:iterate</a> — streaming-friendly loops with state</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xsl-accumulator/">xsl:accumulator</a> — running totals while streaming</li>
<li><a href="https://xsltplayground.com/blog/xslt/functions/xpath-for-each/">for-each()</a> — higher-order functions over sequences</li>
</ul>
<h2>Learn More About XSLT 3.0</h2>
<ul>
<li><a href="https://blog.xsltplayground.com/posts/xslt-3-new-features/">XSLT 3.0 new features: what changed from 2.0</a></li>
<li><a href="https://blog.xsltplayground.com/posts/xslt-result-document/">Streaming and multiple outputs with xsl:result-document</a></li>
<li><a href="https://blog.xsltplayground.com/posts/xslt-xml-to-json-csv/">Transforming XML to JSON and CSV with XSLT 3.0</a></li>
<li><a href="https://xsltplayground.com/blog/posts/xslt-3-new-features/">XSLT 3.0 new features: what changed from 2.0</a></li>
<li><a href="https://xsltplayground.com/blog/posts/xslt-result-document/">Streaming and multiple outputs with xsl:result-document</a></li>
<li><a href="https://xsltplayground.com/blog/posts/xslt-xml-to-json-csv/">Transforming XML to JSON and CSV with XSLT 3.0</a></li>
</ul>
<h2>Also Available</h2>
+29 -6
View File
@@ -1,14 +1,37 @@
---
title: "About"
description: "What XSLT Playground is and how this site supports it."
title: "About XSLT Playground"
description: "The story behind XSLT Playground — a browser-based XSLT editor built for multi-parameter transforms, fast feedback, and practical debugging."
aliases:
- /posts/why-xslt-playground-online-editor/
---
[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.
[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 transforms without heavy desktop tooling.
This site exists to give you:
## Why I built it
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 — a tool that behaves like the lightweight browser utilities we all rely on. So I built XSLT Playground 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.
- Most online options 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 — much like [jqplay.org](https://jqplay.org) did for jq.
### What it delivers
- Multi-input, parameter-rich execution that mirrors enterprise and integration workloads.
- Live runs and inline results so you can iterate without context switching.
- Rough timing so you can spot regressions and optimize hotspots.
- Zero install — it works in the browser, with Saxon HE on the backend for full XSLT 1.0 / 2.0 / 3.0 support.
## What this site gives you
- Release notes and product updates.
- Short guides that mirror common integration tasks.
- Short guides and a full [XSLT & XPath function reference](/xslt/functions/) 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.
Ready to try it? Open [xsltplayground.com](https://xsltplayground.com) and run your next transform right now.
@@ -2,6 +2,7 @@
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
tags: ["xslt", "announcement"]
---
[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.
@@ -1,47 +0,0 @@
---
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.
+4 -4
View File
@@ -111,10 +111,10 @@ The editor runs in your browser, but the transformation itself executes on a Sax
Both extensions are valid and interchangeable. `.xsl` is older and common in enterprise systems; `.xslt` is more explicit. Saxon and XSLT Playground accept either.
**Can I validate an XSL stylesheet online?**
Yes. If your stylesheet is malformed or has a runtime error, the [XSLT validator](https://blog.xsltplayground.com/posts/xslt-validator-online/) reports the exact line number and the original Saxon error message so you can fix it before deploying.
Yes. If your stylesheet is malformed or has a runtime error, the [XSLT validator](/blog/posts/xslt-validator-online/) reports the exact line number and the original Saxon error message so you can fix it before deploying.
## Related guides
- [XSLT online editor: how to test transformations without installing anything](https://blog.xsltplayground.com/posts/xslt-online-editor-guide/)
- [XSLT for beginners: your first transformation](https://blog.xsltplayground.com/posts/xslt-for-beginners/)
- [XSLT validator online: catch errors before running your transform](https://blog.xsltplayground.com/posts/xslt-validator-online/)
- [Open the XSLT Playground editor](https://xsltplayground.com/) — run XSLT 1.0/2.0/3.0 online
- [XSLT for beginners: your first transformation](/blog/posts/xslt-for-beginners/)
- [XSLT validator online: catch errors before running your transform](/blog/posts/xslt-validator-online/)
@@ -2,6 +2,7 @@
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
tags: ["xslt", "debugging", "trace"]
---
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.
@@ -2,6 +2,7 @@
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
tags: ["xslt", "integration", "architecture"]
---
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.
+3 -46
View File
@@ -3,52 +3,9 @@ title: "XSLT online editor: how to test transformations without installing anyth
description: "Run and test XSLT online with a free editor that supports XSLT 1.0, 2.0, and 3.0. No installation, no signup required."
date: 2025-03-15T00:00:00Z
tags: ["xslt", "online", "editor", "tools"]
redirectTo: "https://xsltplayground.com/"
---
Testing XSLT locally means installing a processor, configuring classpaths, and running command-line tools every time you want to check a change. For most day-to-day work — writing a new transform, debugging an output, or verifying a colleague's stylesheet — that overhead is unnecessary. A browser-based XSLT editor removes all of it.
This guide has moved into the tool itself. **[Open the XSLT Playground editor →](https://xsltplayground.com/)** to run and test XSLT 1.0, 2.0, and 3.0 online — no installation, no signup.
## What to look for in an online XSLT editor
Not all browser-based tools are equal. The things that matter in practice:
**XSLT version support.** Many older tools only support XSLT 1.0. If your integration targets 2.0 or 3.0 (grouping, functions, maps, JSON support), you need a tool that runs a proper processor, not a JavaScript port. [XSLT Playground](https://xsltplayground.com) uses Saxon on the backend, which gives you full XSLT 2.0 and 3.0 support including extension functions.
**Multiple inputs and parameters.** Real transforms rarely take a single XML document. You often need a main input plus a reference document, or you need to pass runtime parameters to control output. A good editor lets you define as many inputs and parameters as your stylesheet needs.
**Trace output.** When a transform produces wrong output, you need to see what the processor did. Trace mode shows template firings and variable values step by step, which is far more useful than reading the final output and guessing what went wrong.
**Workspace persistence.** If you close the browser and come back later, your inputs and stylesheet should still be there. Saving to localStorage means you can pick up where you left off without copying everything into a text file.
## Using XSLT Playground
[XSLT Playground](https://xsltplayground.com) covers all of the above. Here is the basic workflow:
1. Paste your XML source into the input panel.
2. Paste your XSLT stylesheet into the stylesheet panel.
3. Set the XSLT version (1.0, 2.0, or 3.0) in the toolbar.
4. Click **Run**. The result appears in the output panel within a second or two.
If the transform fails, error messages appear immediately with line references. Enable trace to see the execution log.
For transforms with parameters, open the parameters panel, add key-value pairs, and they are passed to the stylesheet as external parameters on each run. No need to hardcode them in the stylesheet.
## Sharing and exporting setups
Each workspace in XSLT Playground can be exported as a JSON file. The export includes the stylesheet, input document, parameters, and any trace output. You can send this file to a colleague, and they import it directly — no copy-pasting required.
This is useful for bug reports: instead of describing what went wrong, export the workspace and share the file. The recipient can reproduce the exact input and output in one click.
## When to use an online editor vs a local setup
Use the online editor when:
- You are exploring a new XSLT feature or syntax
- You need to reproduce or share a specific transform issue
- You are working away from your main machine
- You want to quickly verify a change before committing it
Use a local setup when:
- You are processing files that are sensitive or cannot leave your network
- You need to transform very large documents (megabytes or more)
- You are integrating XSLT into a CI pipeline
For everything else, the browser editor is faster and easier to use.
If you are not redirected automatically, follow the link above.
@@ -2,6 +2,7 @@
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
tags: ["xslt", "parameters", "xslt2"]
---
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.
@@ -2,6 +2,7 @@
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
tags: ["xslt", "performance", "keys"]
---
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.
+3 -3
View File
@@ -205,7 +205,7 @@ All of these work in [XSLT Playground](https://xsltplayground.com). Set the vers
## Related guides
- [XSLT grouping with xsl:for-each-group](https://blog.xsltplayground.com/posts/xslt-grouping-for-each-group/) — group and aggregate string values
- [Transforming XML to JSON and CSV with XSLT](https://blog.xsltplayground.com/posts/xslt-xml-to-json-csv/) — `string-join` and `tokenize` in practice
- [XSLT template matching explained](https://blog.xsltplayground.com/posts/xslt-template-matching-explained/) — apply string logic inside template rules
- [XSLT grouping with xsl:for-each-group](/blog/posts/xslt-grouping-for-each-group/) — group and aggregate string values
- [Transforming XML to JSON and CSV with XSLT](/blog/posts/xslt-xml-to-json-csv/) — `string-join` and `tokenize` in practice
- [XSLT template matching explained](/blog/posts/xslt-template-matching-explained/) — apply string logic inside template rules
+7 -30
View File
@@ -36,40 +36,17 @@ To validate a stylesheet quickly:
3. Run with trace enabled
4. Check the error panel for compile-time issues and the trace panel for runtime behaviour
## Common XSLT errors and how to spot them
## What a validator can't catch
**Namespace mismatch**
Your input uses `xmlns="http://example.com/ns"` but your stylesheet matches `element-name` without the namespace. The match never fires, output is empty.
Validation confirms your stylesheet is *well-formed and internally consistent*. It cannot confirm the output is *correct*. A transform that compiles cleanly can still:
Fix: declare the namespace in the stylesheet and use the prefix in match patterns:
```xml
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ex="http://example.com/ns"
xpath-default-namespace="http://example.com/ns">
```
- Produce empty output because a namespace mismatch makes every template match miss.
- Fire the wrong template because two patterns share a priority.
- Serialise as XML when you meant HTML, emitting self-closing tags browsers reject.
Using `xpath-default-namespace` (XSLT 2.0+) avoids having to prefix every element name in your XPath expressions.
None of these are syntax errors, so no validator flags them — you catch them by running the transform against representative input and reading the output (and the trace). Validation narrows the search; it doesn't replace testing.
**Undefined variable**
You reference `$config` but it is only defined inside a conditional branch that did not execute for this input. Saxon reports: *Variable $config has not been assigned a value*.
Fix: move variable declarations to the template root or provide a default:
```xml
<xsl:variable name="config" select="if (config) then config else 'default'"/>
```
**Wrong output method**
You are generating HTML but the processor serialises as XML, adding self-closing tags that browsers reject. Declare the output method explicitly:
```xml
<xsl:output method="html" version="5" encoding="UTF-8" indent="yes"/>
```
**Template priority conflict**
Two templates match the same node with equal priority. Saxon signals an error rather than silently picking one. Assign explicit `priority` attributes to resolve the conflict:
```xml
<xsl:template match="item[@type='special']" priority="1">
```
When a run *does* raise an error, it comes back as a Saxon code like `XPST0017` or `XPTY0004`. Each one has a specific, repeatable fix — see the [XSLT error messages reference](/posts/xslt-common-errors/) for the 17 most common Saxon errors and how to resolve each, and the [XSLT debugging patterns](/posts/xslt-debugging-patterns/) guide for isolating the ones that only show up at runtime.
## Validating before deploying to production
+1 -1
View File
@@ -77,4 +77,4 @@ Yes, and mature integration estates do exactly that: DataWeave for Mule-internal
## Try the XSLT side in 30 seconds
You don't need a platform install to evaluate XSLT: paste the grouping example above into [XSLT Playground](https://xsltplayground.com/) — a free online editor running real Saxon (XSLT 1.0/2.0/3.0), with multiple inputs, parameters and an execution trace. The [function reference](https://blog.xsltplayground.com/xslt/functions/) covers everything available in each version.
You don't need a platform install to evaluate XSLT: paste the grouping example above into [XSLT Playground](https://xsltplayground.com/) — a free online editor running real Saxon (XSLT 1.0/2.0/3.0), with multiple inputs, parameters and an execution trace. The [function reference](/blog/xslt/functions/) covers everything available in each version.
@@ -2,6 +2,7 @@
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
tags: ["xslt", "json", "csv", "xslt3"]
---
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.
+1 -1
View File
@@ -23,4 +23,4 @@ A call to `doc()`, `document()` or `unparsed-text()` failed to retrieve the reso
- Guard the lookup with `doc-available()` before calling `doc()`.
- Verify the base URI with `base-uri(/)` and `static-base-uri()` — print them with `xsl:message`.
- In [XSLT Playground](https://xsltplayground.com/), don't use `doc()` for secondary inputs: pass extra documents as **named parameters** in the Input panel instead — see [working with parameters and multiple inputs](https://blog.xsltplayground.com/posts/xslt-parameters-and-multiple-inputs/).
- In [XSLT Playground](https://xsltplayground.com/), don't use `doc()` for secondary inputs: pass extra documents as **named parameters** in the Input panel instead — see [working with parameters and multiple inputs](/blog/posts/xslt-parameters-and-multiple-inputs/).
+1 -1
View File
@@ -25,4 +25,4 @@ Make the choice explicit instead of relying on declaration order:
- Add an explicit **`priority`**: `<xsl:template match="item" priority="2">`.
- Use **modes** to separate concerns: `<xsl:template match="item" mode="summary">`.
- Delete the leftover duplicate if it is dead code.
- To *find* which templates collide, run with tracing in [XSLT Playground](https://xsltplayground.com/) — the trace shows which template fired for each node. See also [template matching explained](https://blog.xsltplayground.com/posts/xslt-template-matching-explained/).
- To *find* which templates collide, run with tracing in [XSLT Playground](https://xsltplayground.com/) — the trace shows which template fired for each node. See also [template matching explained](/blog/posts/xslt-template-matching-explained/).
+17 -1
View File
@@ -1,4 +1,4 @@
baseURL = "https://blog.xsltplayground.com/"
baseURL = "https://xsltplayground.com/blog/"
languageCode = "en-us"
title = "XSLT Playground"
@@ -7,6 +7,22 @@ enableRobotsTXT = true
[pagination]
pagerSize = 10
# Drives the "Related articles" box. Without this, related-posts.html fell back
# to "first 4 posts", so 12/20 posts had zero inbound internal links.
[related]
includeNewer = true
threshold = 20
toLower = true
[[related.indices]]
name = "tags"
weight = 100
[[related.indices]]
name = "keywords"
weight = 80
[[related.indices]]
name = "category"
weight = 40
[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."
+8
View File
@@ -6,7 +6,15 @@
<meta name="google-adsense-account" content="ca-pub-1549720748100858">
<title>{{ block "title" . }}{{ with .Title }}{{ . }} | {{ end }}{{ .Site.Title }}{{ end }}</title>
<meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.subtitle }}{{ end }}">
{{- if .Params.redirectTo -}}
{{- /* Soft 301 for retired/cannibalizing pages. GitHub Pages can't emit a real
301, so meta-refresh + noindex,follow lets Google drop the thin page and
pass its equity through. A real 301 will come from the NPM proxy later. */ -}}
<meta http-equiv="refresh" content="0; url={{ .Params.redirectTo }}">
<meta name="robots" content="noindex, follow">
{{- else -}}
<link rel="canonical" href="{{ .Permalink }}">
{{- end }}
<meta property="og:title" content="{{ with .Title }}{{ . }} | {{ end }}{{ .Site.Title }}">
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.subtitle }}{{ end }}">
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
+12 -1
View File
@@ -1,4 +1,15 @@
{{- $related := first 4 (where (where .Site.RegularPages "Section" "posts") "Permalink" "ne" .Permalink) -}}
{{- /* Real topical relatedness via Hugo's [related] indices (tags/keywords/category).
Always surfaces blog posts (Section "posts") so reference pages funnel equity
into the articles. Falls back to recent posts only when nothing matches, so the
box is never empty. */ -}}
{{- $posts := where .Site.RegularPages "Section" "posts" -}}
{{- $related := where (.Site.RegularPages.Related .) "Section" "posts" -}}
{{- $related = where $related "Permalink" "ne" .Permalink -}}
{{- if lt (len $related) 4 -}}
{{- $fallback := where $posts "Permalink" "ne" .Permalink -}}
{{- $related = $related | append $fallback | uniq -}}
{{- end -}}
{{- $related = first 4 $related -}}
{{- if $related -}}
<aside class="related-posts">
<h3>Related articles</h3>