mirror of
https://github.com/alexandrev/xslt-lab.git
synced 2026-09-16 18:23:16 +00:00
SEO improvements and 4 new blog articles
- Update index.html title, description, keywords to include xslt 3.0, validator, saxon - Update JSON-LD schema with full feature list and XSLT 3.0 - Change footer link from "News" to "Blog & Tutorials", update URL to blog.xsltplayground.com - New articles: online editor guide, validator guide, string functions, xsl:for-each-group Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+12
-9
@@ -3,19 +3,19 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>XSLT Playground - Online XSLT Editor & Tester</title>
|
||||
<title>XSLT Playground - Free Online XSLT Editor, Tester & Validator</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Free online XSLT editor, tester and optimizer. Experiment with XML transformations in our interactive XSLT playground and optimize your stylesheets."
|
||||
content="Free online XSLT editor, tester and validator. Run XSLT 1.0, 2.0 and 3.0 transformations instantly in your browser using Saxon. No installation required."
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="xslt, xslt editor, xsl editor, xsl tester, xslt tester, xsl playground, xslt playground, xslt optimizer, xslt tool, xml transformation"
|
||||
content="xslt online, xslt editor, xslt tester, xslt validator, xsl editor, xsl tester, xslt playground, xslt 3.0, xslt 2.0, online xslt editor, xslt transformation online, xml transformation, saxon xslt"
|
||||
/>
|
||||
<meta property="og:title" content="XSLT Playground - Online XSLT Editor & Tester" />
|
||||
<meta property="og:title" content="XSLT Playground - Free Online XSLT Editor, Tester & Validator" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Free online XSLT editor, tester and optimizer. Experiment with XML transformations in our interactive XSLT playground and optimize your stylesheets."
|
||||
content="Free online XSLT editor, tester and validator. Run XSLT 1.0, 2.0 and 3.0 transformations instantly in your browser using Saxon. No installation required."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://xsltplayground.com/" />
|
||||
@@ -29,7 +29,7 @@
|
||||
"name": "XSLT Playground",
|
||||
"alternateName": "XSLT Lab",
|
||||
"url": "https://xsltplayground.com/",
|
||||
"description": "Free online XSLT editor, tester and optimizer. Transform XML documents with XSLT 1.0 and 2.0 stylesheets using Saxon processor.",
|
||||
"description": "Free online XSLT editor, tester and validator. Run XSLT 1.0, 2.0 and 3.0 transformations instantly in your browser using Saxon processor. No installation required.",
|
||||
"applicationCategory": "DeveloperApplication",
|
||||
"operatingSystem": "Any",
|
||||
"offers": {
|
||||
@@ -38,11 +38,14 @@
|
||||
"priceCurrency": "USD"
|
||||
},
|
||||
"featureList": [
|
||||
"XSLT 1.0 and 2.0 support",
|
||||
"XSLT 1.0, 2.0 and 3.0 support",
|
||||
"Saxon-powered backend",
|
||||
"Real-time XML transformation",
|
||||
"XSLT validator with detailed error messages",
|
||||
"Multiple workspaces",
|
||||
"Variable tracing",
|
||||
"Import/Export workspaces"
|
||||
"Execution trace and debugging",
|
||||
"Multiple XML inputs and parameters",
|
||||
"Import/Export workspaces as JSON"
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -65,7 +65,7 @@ const adsenseSlot = env.VITE_ADSENSE_SLOT;
|
||||
const ethicalAdsPublisher = env.VITE_ETHICALADS_PUBLISHER || "xsltplaygroundcom";
|
||||
const defaultRepoUrl = "https://github.com/alexandrev/xslt-lab";
|
||||
const repoUrl = env.VITE_REPO_URL || defaultRepoUrl;
|
||||
const newsUrl = env.VITE_NEWS_URL || "https://alexandrev.github.io/xslt-lab/";
|
||||
const newsUrl = env.VITE_NEWS_URL || "https://blog.xsltplayground.com/";
|
||||
const resolvedVersion =
|
||||
typeof __APP_VERSION__ !== "undefined" && __APP_VERSION__
|
||||
? __APP_VERSION__
|
||||
@@ -1902,7 +1902,7 @@ export default function App() {
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
News
|
||||
Blog & Tutorials
|
||||
</a>
|
||||
{resolvedVersion && (
|
||||
<a
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
---
|
||||
title: "XSLT grouping with xsl:for-each-group: complete guide"
|
||||
description: "How to group XML nodes in XSLT 2.0 using for-each-group. Covers group-by, group-adjacent, group-starting-with, and group-ending-with with examples."
|
||||
date: 2025-04-01T00:00:00Z
|
||||
tags: ["xslt", "grouping", "xslt2", "xpath"]
|
||||
---
|
||||
|
||||
Grouping is one of the most powerful features introduced in XSLT 2.0. Before it, grouping in XSLT 1.0 required the Muenchian method — a clever but verbose technique involving keys and node-set comparisons. In 2.0, `xsl:for-each-group` makes grouping straightforward.
|
||||
|
||||
## Basic grouping with group-by
|
||||
|
||||
`group-by` groups nodes that share the same value for a given expression. The result is one iteration per distinct group value.
|
||||
|
||||
**Input:**
|
||||
```xml
|
||||
<orders>
|
||||
<order><country>DE</country><amount>120</amount></order>
|
||||
<order><country>US</country><amount>85</amount></order>
|
||||
<order><country>DE</country><amount>200</amount></order>
|
||||
<order><country>FR</country><amount>60</amount></order>
|
||||
<order><country>US</country><amount>140</amount></order>
|
||||
</orders>
|
||||
```
|
||||
|
||||
**Stylesheet:**
|
||||
```xml
|
||||
<xsl:template match="orders">
|
||||
<summary>
|
||||
<xsl:for-each-group select="order" group-by="country">
|
||||
<xsl:sort select="current-grouping-key()"/>
|
||||
<group country="{current-grouping-key()}">
|
||||
<count><xsl:value-of select="count(current-group())"/></count>
|
||||
<total><xsl:value-of select="sum(current-group()/amount)"/></total>
|
||||
</group>
|
||||
</xsl:for-each-group>
|
||||
</summary>
|
||||
</xsl:template>
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```xml
|
||||
<summary>
|
||||
<group country="DE"><count>2</count><total>320</total></group>
|
||||
<group country="FR"><count>1</count><total>60</total></group>
|
||||
<group country="US"><count>2</count><total>225</total></group>
|
||||
</summary>
|
||||
```
|
||||
|
||||
Key functions inside `for-each-group`:
|
||||
- `current-grouping-key()` — returns the value that defines the current group
|
||||
- `current-group()` — returns the sequence of all nodes in the current group
|
||||
|
||||
## Nested grouping
|
||||
|
||||
Groups can be nested. Group orders by country, then within each country by status:
|
||||
|
||||
```xml
|
||||
<xsl:for-each-group select="order" group-by="country">
|
||||
<country name="{current-grouping-key()}">
|
||||
<xsl:for-each-group select="current-group()" group-by="status">
|
||||
<status value="{current-grouping-key()}">
|
||||
<xsl:value-of select="count(current-group())"/>
|
||||
</status>
|
||||
</xsl:for-each-group>
|
||||
</country>
|
||||
</xsl:for-each-group>
|
||||
```
|
||||
|
||||
## group-adjacent
|
||||
|
||||
Groups consecutive nodes that share the same key value. Unlike `group-by`, it starts a new group when the key changes, even if the same key appeared earlier. This is useful for processing structured text or segmented data.
|
||||
|
||||
```xml
|
||||
<log>
|
||||
<entry level="INFO">Starting</entry>
|
||||
<entry level="INFO">Processing</entry>
|
||||
<entry level="ERROR">Failed</entry>
|
||||
<entry level="ERROR">Retrying</entry>
|
||||
<entry level="INFO">Done</entry>
|
||||
</log>
|
||||
```
|
||||
|
||||
```xml
|
||||
<xsl:for-each-group select="entry" group-adjacent="@level">
|
||||
<block level="{current-grouping-key()}" count="{count(current-group())}">
|
||||
<xsl:value-of select="current-group()[1]"/>
|
||||
</block>
|
||||
</xsl:for-each-group>
|
||||
```
|
||||
|
||||
This produces three blocks: two INFO (positions 1-2), one ERROR (3-4), one INFO (5). With `group-by`, the two INFO groups would be merged into one.
|
||||
|
||||
## group-starting-with and group-ending-with
|
||||
|
||||
These group nodes based on a pattern match rather than a key value. Every time a node matches the pattern, a new group starts (or ends).
|
||||
|
||||
**group-starting-with example** — treat every `<h2>` as the start of a section:
|
||||
|
||||
```xml
|
||||
<xsl:for-each-group select="*" group-starting-with="h2">
|
||||
<section>
|
||||
<title><xsl:value-of select="self::h2"/></title>
|
||||
<xsl:apply-templates select="current-group()[position() > 1]"/>
|
||||
</section>
|
||||
</xsl:for-each-group>
|
||||
```
|
||||
|
||||
**group-ending-with example** — group lines until a blank line:
|
||||
|
||||
```xml
|
||||
<xsl:for-each-group select="line" group-ending-with="line[. = '']">
|
||||
<paragraph>
|
||||
<xsl:apply-templates select="current-group()[. != '']"/>
|
||||
</paragraph>
|
||||
</xsl:for-each-group>
|
||||
```
|
||||
|
||||
## Computing aggregates
|
||||
|
||||
`current-group()` returns a sequence, so you can apply any XPath aggregate function directly:
|
||||
|
||||
```xml
|
||||
<xsl:for-each-group select="transaction" group-by="currency">
|
||||
<currency code="{current-grouping-key()}">
|
||||
<count><xsl:value-of select="count(current-group())"/></count>
|
||||
<total><xsl:value-of select="sum(current-group()/amount)"/></total>
|
||||
<average><xsl:value-of select="avg(current-group()/amount)"/></average>
|
||||
<max><xsl:value-of select="max(current-group()/amount)"/></max>
|
||||
</currency>
|
||||
</xsl:for-each-group>
|
||||
```
|
||||
|
||||
## Try it in XSLT Playground
|
||||
|
||||
Paste any of the examples above into [XSLT Playground](https://xsltplayground.com) with version set to 2.0 or 3.0. Grouping is one of the features that benefits most from live testing — you can immediately see how changing the grouping key or switching between `group-by` and `group-adjacent` affects the output structure.
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: "XSLT online editor: how to test transformations without installing anything"
|
||||
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"]
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,180 @@
|
||||
---
|
||||
title: "XSLT string functions: complete reference with examples"
|
||||
description: "Complete guide to XSLT and XPath string functions: substring, contains, replace, tokenize, normalize-space and more. With practical examples."
|
||||
date: 2025-03-28T00:00:00Z
|
||||
tags: ["xslt", "xpath", "strings", "functions"]
|
||||
---
|
||||
|
||||
String manipulation is one of the most common tasks in XSLT. Whether you are formatting output, parsing codes, or normalising values from external systems, XPath provides a rich set of string functions. This reference covers the most useful ones with examples you can run in [XSLT Playground](https://xsltplayground.com).
|
||||
|
||||
## Basic string functions (XSLT 1.0+)
|
||||
|
||||
### string-length
|
||||
|
||||
Returns the number of characters in a string.
|
||||
|
||||
```xml
|
||||
<xsl:value-of select="string-length('hello')"/> <!-- 5 -->
|
||||
<xsl:value-of select="string-length(title)"/> <!-- length of title element text -->
|
||||
```
|
||||
|
||||
### substring
|
||||
|
||||
Extracts a portion of a string. Arguments: string, start position (1-based), optional length.
|
||||
|
||||
```xml
|
||||
<xsl:value-of select="substring('ABCDEF', 2, 3)"/> <!-- BCD -->
|
||||
<xsl:value-of select="substring('ABCDEF', 4)"/> <!-- DEF -->
|
||||
```
|
||||
|
||||
### substring-before and substring-after
|
||||
|
||||
Split a string on a delimiter.
|
||||
|
||||
```xml
|
||||
<xsl:value-of select="substring-before('2024-11-28', '-')"/> <!-- 2024 -->
|
||||
<xsl:value-of select="substring-after('user@example.com', '@')"/> <!-- example.com -->
|
||||
```
|
||||
|
||||
### contains, starts-with, ends-with
|
||||
|
||||
Test membership without extracting.
|
||||
|
||||
```xml
|
||||
<xsl:if test="contains(email, '@')">...</xsl:if>
|
||||
<xsl:if test="starts-with(code, 'EUR')">...</xsl:if>
|
||||
<!-- ends-with requires XSLT 2.0+ -->
|
||||
<xsl:if test="ends-with(filename, '.xml')">...</xsl:if>
|
||||
```
|
||||
|
||||
### concat
|
||||
|
||||
Joins strings together. Takes any number of arguments.
|
||||
|
||||
```xml
|
||||
<xsl:value-of select="concat(firstname, ' ', lastname)"/>
|
||||
<xsl:value-of select="concat('ID-', format-number(id, '0000'))"/>
|
||||
```
|
||||
|
||||
### normalize-space
|
||||
|
||||
Strips leading and trailing whitespace, and collapses internal whitespace to single spaces. Essential for cleaning values from XML sources.
|
||||
|
||||
```xml
|
||||
<xsl:value-of select="normalize-space(description)"/>
|
||||
```
|
||||
|
||||
### translate
|
||||
|
||||
Replaces characters one-for-one. Useful for simple case conversion or character removal.
|
||||
|
||||
```xml
|
||||
<!-- uppercase (XSLT 1.0 approach) -->
|
||||
<xsl:value-of select="translate(name, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
|
||||
|
||||
<!-- remove digits -->
|
||||
<xsl:value-of select="translate(code, '0123456789', '')"/>
|
||||
```
|
||||
|
||||
## Advanced string functions (XSLT 2.0+)
|
||||
|
||||
### upper-case and lower-case
|
||||
|
||||
No longer need `translate` for case conversion.
|
||||
|
||||
```xml
|
||||
<xsl:value-of select="upper-case(name)"/>
|
||||
<xsl:value-of select="lower-case(status)"/>
|
||||
```
|
||||
|
||||
### replace
|
||||
|
||||
Regex-based substitution. Much more powerful than `translate`.
|
||||
|
||||
```xml
|
||||
<!-- remove all non-digits -->
|
||||
<xsl:value-of select="replace(phone, '[^0-9]', '')"/>
|
||||
|
||||
<!-- normalise whitespace to single space -->
|
||||
<xsl:value-of select="replace(description, '\s+', ' ')"/>
|
||||
|
||||
<!-- mask last 4 digits of card number -->
|
||||
<xsl:value-of select="replace(card, '\d(?=\d{4})', '*')"/>
|
||||
```
|
||||
|
||||
### matches
|
||||
|
||||
Tests a string against a regex.
|
||||
|
||||
```xml
|
||||
<xsl:if test="matches(email, '^[^@]+@[^@]+\.[^@]+$')">
|
||||
<!-- valid-looking email -->
|
||||
</xsl:if>
|
||||
```
|
||||
|
||||
### tokenize
|
||||
|
||||
Splits a string into a sequence using a regex delimiter. Returns a sequence of strings.
|
||||
|
||||
```xml
|
||||
<!-- split CSV line -->
|
||||
<xsl:for-each select="tokenize(csv-line, ',')">
|
||||
<item><xsl:value-of select="normalize-space(.)"/></item>
|
||||
</xsl:for-each>
|
||||
|
||||
<!-- split on any whitespace -->
|
||||
<xsl:variable name="words" select="tokenize(sentence, '\s+')"/>
|
||||
<xsl:value-of select="count($words)"/> <!-- word count -->
|
||||
```
|
||||
|
||||
### string-join
|
||||
|
||||
The inverse of `tokenize`. Joins a sequence with a separator.
|
||||
|
||||
```xml
|
||||
<xsl:value-of select="string-join(//tag/text(), ', ')"/>
|
||||
<!-- "xml, xslt, saxon" -->
|
||||
```
|
||||
|
||||
### format-number
|
||||
|
||||
Formats a number as a string with a picture pattern.
|
||||
|
||||
```xml
|
||||
<xsl:value-of select="format-number(1234567.89, '#,##0.00')"/>
|
||||
<!-- 1,234,567.89 -->
|
||||
<xsl:value-of select="format-number(0.175, '0.00%')"/>
|
||||
<!-- 17.50% -->
|
||||
```
|
||||
|
||||
### format-date and format-dateTime
|
||||
|
||||
Format xs:date and xs:dateTime values using picture strings.
|
||||
|
||||
```xml
|
||||
<xsl:value-of select="format-date(xs:date('2024-11-28'), '[D01]/[M01]/[Y]')"/>
|
||||
<!-- 28/11/2024 -->
|
||||
|
||||
<xsl:value-of select="format-dateTime(current-dateTime(), '[H01]:[m01]:[s01]')"/>
|
||||
<!-- 14:35:22 -->
|
||||
```
|
||||
|
||||
## Practical patterns
|
||||
|
||||
**Extract domain from URL:**
|
||||
```xml
|
||||
<xsl:variable name="after-proto" select="substring-after(url, '//')"/>
|
||||
<xsl:value-of select="substring-before($after-proto, '/')"/>
|
||||
```
|
||||
|
||||
**Pad a number with leading zeros:**
|
||||
```xml
|
||||
<xsl:value-of select="format-number(id, '000000')"/>
|
||||
```
|
||||
|
||||
**Check if a node text is numeric:**
|
||||
```xml
|
||||
<xsl:if test="matches(normalize-space(amount), '^\d+(\.\d+)?$')">
|
||||
```
|
||||
|
||||
All of these work in [XSLT Playground](https://xsltplayground.com). Set the version to 2.0 or 3.0 for the functions that require it.
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: "XSLT validator online: catch errors before running your transform"
|
||||
description: "How to validate XSLT stylesheets online. Catch syntax errors, undefined variables, and namespace issues before they hit production."
|
||||
date: 2025-03-22T00:00:00Z
|
||||
tags: ["xslt", "validation", "debugging", "tools"]
|
||||
---
|
||||
|
||||
A broken XSLT stylesheet can fail in several ways: a syntax error stops the processor immediately, a namespace mismatch silently produces empty output, or an undefined variable causes a runtime error that only appears with specific inputs. Catching these issues early, before the stylesheet reaches a test environment, saves significant debugging time.
|
||||
|
||||
## What XSLT validation actually checks
|
||||
|
||||
XSLT validation happens at two levels:
|
||||
|
||||
**Compile-time checks** happen when the processor parses the stylesheet. These catch:
|
||||
- Malformed XML in the stylesheet itself
|
||||
- References to undefined named templates or functions
|
||||
- Type errors in static expressions
|
||||
- Invalid XSLT element usage (wrong attributes, missing required children)
|
||||
|
||||
**Runtime errors** only appear when the stylesheet runs against actual input:
|
||||
- Missing nodes that are assumed to exist
|
||||
- Type errors in dynamic expressions
|
||||
- Namespace mismatches between the stylesheet and the input document
|
||||
|
||||
A good validator runs both levels.
|
||||
|
||||
## Using XSLT Playground as a validator
|
||||
|
||||
[XSLT Playground](https://xsltplayground.com) runs Saxon, which is one of the most thorough XSLT processors available. When you paste a stylesheet and click Run, Saxon compiles it first and reports compile errors with exact line numbers before attempting execution.
|
||||
|
||||
For runtime errors, the trace mode shows you exactly which template fired, which node was being processed, and where the failure occurred. This is more useful than a bare error message because it gives you the execution context.
|
||||
|
||||
To validate a stylesheet quickly:
|
||||
1. Paste the stylesheet into the editor
|
||||
2. Provide a minimal XML input — even an empty `<root/>` catches most compile errors
|
||||
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
|
||||
|
||||
**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.
|
||||
|
||||
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">
|
||||
```
|
||||
|
||||
Using `xpath-default-namespace` (XSLT 2.0+) avoids having to prefix every element name in your XPath expressions.
|
||||
|
||||
**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">
|
||||
```
|
||||
|
||||
## Validating before deploying to production
|
||||
|
||||
If you run XSLT as part of an integration pipeline (MuleSoft, Tibco, IBM DataPower, or a custom backend), test the stylesheet in [XSLT Playground](https://xsltplayground.com) against representative inputs before deploying. Saxon in the playground uses the same processor your backend may be running, so errors caught here are errors caught before production.
|
||||
|
||||
Export the workspace as JSON and keep it as a regression test artifact. If a future change breaks the transform, you have the original inputs and expected output to compare against.
|
||||
Reference in New Issue
Block a user