Chapter 70: XSLT Examples
XSLT Examples tutorial, written as if I’m your personal teacher sitting next to you with a whiteboard, explaining everything slowly and patiently.
We will go step-by-step through progressively more realistic and useful examples — from the absolute simplest “hello world” transformation to real-world patterns that companies actually use in production in 2025–2026.
Every example includes:
- the input XML
- the XSLT code (with line-by-line comments)
- the expected output
- explanation of what happens inside the processor
- common beginner mistakes & how to avoid them
- why this pattern is useful in real projects
Preparation – Tools you can use right now to test
- Online XSLT tester (quick & no setup)
- https://www.freeformatter.com/xslt-transformer.html
- https://www.utilities-online.info/xslt/
- https://xsltfiddle.liberty-development.net/
- Local setup (recommended for serious learning)
- Oxygen XML Editor (free trial / academic license)
- VS Code + “XSLT/XPath” extension + Saxon-HE jar
- Command line: xsltproc (Linux/Mac) or Saxon (java -jar saxon-he-12.5.jar -s:input.xml -xsl:style.xsl -o:output.html)
Let’s start.
Example 1 – The absolute minimal XSLT (Hello World style)
Goal: No matter what the input XML is, always output the same HTML page.
Input XML (can be anything – even empty)
|
0 1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="UTF-8"?> <nothing/> |
XSLT code (minimal.xsl)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- Tell XSLT processor: output HTML --> <xsl:output method="html" indent="yes" encoding="UTF-8"/> <!-- This template matches the root of ANY input document --> <xsl:template match="/"> <html> <head> <title>Hello from XSLT</title> </head> <body> <h1>This page was generated by XSLT</h1> <p>Even if your input XML is empty, I still produce this HTML.</p> <p>Current date/time: <xsl:value-of select="current-dateTime()"/></p> </body> </html> </xsl:template> </xsl:stylesheet> |
What happens inside the processor?
- It starts reading any input XML
- It immediately looks for a template that matches the root (/)
- Finds this one → runs it
- Outputs the HTML — no data from input is used
Real-world use case for this pattern
- “Landing page” / error page when XML is invalid
- Fallback HTML when transformation fails
- Simple “XML received” confirmation page
Example 2 – Identity Transform (copy XML almost unchanged)
Goal: Copy the entire input XML → add one small modification
Input XML
|
0 1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="UTF-8"?> <invoice number="INV-2025-0789"> <date>2025-07-28</date> <customer>TechTrend Innovations</customer> <amount currency="INR">271400.00</amount> </invoice> |
XSLT – copy everything + add a <processed> timestamp
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- ===================== --> <!-- IDENTITY TEMPLATE --> <!-- Copy everything by default --> <!-- ===================== --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- ===================== --> <!-- Special rule: add <processed> after <date> --> <!-- ===================== --> <xsl:template match="date"> <xsl:copy-of select="."/> <!-- Insert new element right after <date> --> <processed-at> <xsl:value-of select="current-dateTime()"/> </processed-at> </xsl:template> </xsl:stylesheet> |
Output XML
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="UTF-8"?> <invoice number="INV-2025-0789"> <date>2025-07-28</date> <processed-at>2025-07-28T14:35:22.123+05:30</processed-at> <customer>TechTrend Innovations</customer> <amount currency="INR">271400.00</amount> </invoice> |
Why this pattern is used in real life (2025–2026)
- Add audit timestamp / processed-by / version stamp
- Add digital signature placeholder
- Add missing required fields (compliance)
- Remove sensitive fields (data masking)
- Rename tags / namespaces for different partner
Example 3 – Real-world: Enrich GST invoice with extra info
Input (simplified GST invoice XML)
|
0 1 2 3 4 5 6 7 8 9 10 11 |
<Invoice> <ID>INV/2025/0789</ID> <IssueDate>2025-07-28</IssueDate> <SupplierGSTIN>36AAECT4567P1Z2</SupplierGSTIN> <Total>271400.00</Total> </Invoice> |
Goal: Add:
- Current processing timestamp
- Tax category message
- QR code placeholder
XSLT
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- Identity – copy everything --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- Add processing info after <IssueDate> --> <xsl:template match="IssueDate"> <xsl:copy-of select="."/> <ProcessedAt> <xsl:value-of select="current-dateTime()"/> </ProcessedAt> <TaxCategory> <xsl:choose> <xsl:when test="../Total > 100000">High Value Invoice – Additional checks required</xsl:when> <xsl:otherwise>Standard Invoice</xsl:otherwise> </xsl:choose> </TaxCategory> <QRCodePlaceholder>QR code will be generated here</QRCodePlaceholder> </xsl:template> </xsl:stylesheet> |
Result (new fields added after IssueDate)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<Invoice> <ID>INV/2025/0789</ID> <IssueDate>2025-07-28</IssueDate> <ProcessedAt>2025-07-28T14:35:22.123+05:30</ProcessedAt> <TaxCategory>High Value Invoice – Additional checks required</TaxCategory> <QRCodePlaceholder>QR code will be generated here</QRCodePlaceholder> <SupplierGSTIN>...</SupplierGSTIN> <Total>271400.00</Total> </Invoice> |
Lesson 4 – Common “edit XML” tasks & how XSLT does them
| Task | XSLT pattern / technique | Key code snippet |
|---|---|---|
| Add element after specific tag | Match preceding tag → copy it + add new element | <xsl:template match=”IssueDate”> <xsl:copy-of select=”.”/> <newTag>…</newTag> </xsl:template> |
| Add attribute to existing element | Match element → copy + add <xsl:attribute> | <xsl:template match=”Total”> <xsl:copy> <xsl:attribute name=”currency”>INR</xsl:attribute> <xsl:apply-templates/> </xsl:copy> </xsl:template> |
| Change existing element text | Match element → output new value | <xsl:template match=”Note”> <Note>Updated note</Note> </xsl:template> |
| Remove element conditionally | Match element + condition → output nothing | <xsl:template match=”TaxTotal[TaxAmount=0]”/> |
| Rename element | Match old name → output new tag + copy content | `<xsl:template match=”PayableAmount”> <GrandTotal> <xsl:copy-of select=”@* |
| Move element to different position | Copy it → remove original → paste copy | Two templates: copy + skip original |
| Add namespace / schema location | Add to root using <xsl:copy> + <xsl:attribute> | Very common for compliance |
Lesson 5 – Try yourself exercises (do these!)
- Add a <ProcessedBy>System XSLT v3.0</ProcessedBy> element after <IssueDate>
- Change all <price> currency to “USD”
- Remove <stock> element if value = 0
- Rename <price> to <unitPrice>
- Add <Discount>0.00</Discount> before <price>
- Move <author> to be the first child of <book>
Lesson 6 – Real-world context (2025–2026)
Most “edit XML” XSLT today happens on servers in:
- GST / e-Invoice portals — add IRN, QR code, signed properties
- PEPPOL / Factur-X / ZUGFeRD — enrich with PDF attachment references
- ISO 20022 migration — add missing fields, convert currency
- HL7 CDA — add confidentiality codes, normalize dates
- ERP → partner integration — rename tags, add partner-specific fields
- Data masking — remove personal information before sharing
Would you like to continue with one of these next?
- Identity transform + conditional removal (remove sensitive data)
- Adding digital signature placeholder / QR code
- Enriching with calculated fields (tax breakdown, totals)
- Namespace / schemaLocation changes
- Multi-step transformation (XSLT → another XSLT)
- Debugging when changes don’t appear or XML becomes invalid
Just tell me which direction feels most useful or interesting for you right now! 😊
