Chapter 69: XSLT Editing XML
1. The honest reality in 2025–2026: “Editing XML” with XSLT
When people say “edit XML with XSLT”, they almost never mean:
- Open file → change one line → save
They mean:
Take an existing XML document Make small or large modifications to it (add/remove/change elements/attributes/text) Produce a new (modified) XML document as output
This is called the identity transform + modifications pattern — the single most common real-world use case of XSLT in 2025–2026.
Why? Because:
- Many enterprise/government/finance/healthcare systems require XML as input and output
- You often need to add missing fields, remove sensitive data, rename tags, add digital signatures, convert namespaces, filter content, enrich with extra metadata…
- Doing this with string manipulation (regex, DOM manipulation in Java/Python) is fragile and error-prone
- XSLT was designed exactly for this — safe, declarative, schema-aware transformation
2. The most important pattern: Identity Transform + Modifications
Identity transform = copy everything as-is, unless told otherwise.
This is the foundation of almost every “edit XML” stylesheet.
Classic identity template (XSLT 1.0 style)
|
0 1 2 3 4 5 6 7 8 9 10 11 |
<!-- Copy everything by default --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> |
- @* = all attributes
- node() = all nodes (elements, text, comments, processing instructions…)
- <xsl:copy> = copy current node (tag name, attributes, namespace)
- <xsl:apply-templates> = continue processing children
Result: the output is identical to the input — unless you add more specific templates that override parts.
3. Full realistic example – Common “edit XML” tasks
Input XML (before modification – invoice.xml)
|
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 |
<?xml version="1.0" encoding="UTF-8"?> <Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"> <cbc:ID>INV/2025/0789</cbc:ID> <cbc:IssueDate>2025-07-28</cbc:IssueDate> <cac:AccountingSupplierParty> <cac:Party> <cbc:Name>TechTrend Innovations</cbc:Name> <cac:PartyIdentification> <cbc:ID schemeID="GSTIN">36AAECT4567P1Z2</cbc:ID> </cac:PartyIdentification> </cac:Party> </cac:AccountingSupplierParty> <cac:TaxTotal> <cbc:TaxAmount currencyID="INR">41400.00</cbc:TaxAmount> </cac:TaxTotal> <cbc:PayableAmount currencyID="INR">271400.00</cbc:PayableAmount> </Invoice> |
Common editing tasks we will do (one by one):
- Add a new element <cbc:Note> with a message
- Change <cbc:IssueDate> to current date
- Add an attribute issueTime to <cbc:IssueDate>
- Remove <cac:TaxTotal> if tax amount = 0
- Rename <cbc:PayableAmount> to <cbc:GrandTotal>
- Add a digital signature placeholder element
XSLT stylesheet (edit-invoice.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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" exclude-result-prefixes="cbc cac"> <!-- Output settings --> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- ===================== --> <!-- IDENTITY TRANSFORM --> <!-- Copy everything by default --> <!-- ===================== --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- ===================== --> <!-- 1. Add new <cbc:Note> after <cbc:IssueDate> --> <!-- ===================== --> <xsl:template match="cbc:IssueDate"> <xsl:copy-of select="."/> <!-- Insert new note right after the date --> <cbc:Note>Payment due within 15 days. Thank you for your business!</cbc:Note> </xsl:template> <!-- ===================== --> <!-- 2. Change <cbc:IssueDate> to current date --> <!-- ===================== --> <xsl:template match="cbc:IssueDate"> <cbc:IssueDate> <xsl:value-of select="format-date(current-date(), '[Y0001]-[M01]-[D01]')"/> </cbc:IssueDate> </xsl:template> <!-- ===================== --> <!-- 3. Add attribute issueTime to <cbc:IssueDate> --> <!-- ===================== --> <xsl:template match="cbc:IssueDate"> <cbc:IssueDate issueTime="{format-dateTime(current-dateTime(), '[H01]:[m01]:[s01]')}"> <xsl:apply-templates select="@*|node()"/> </cbc:IssueDate> </xsl:template> <!-- ===================== --> <!-- 4. Remove <cac:TaxTotal> if amount = 0 --> <!-- ===================== --> <xsl:template match="cac:TaxTotal[cbc:TaxAmount = 0]"> <!-- Do nothing → element is removed --> </xsl:template> <!-- ===================== --> <!-- 5. Rename <cbc:PayableAmount> to <cbc:GrandTotal> --> <!-- ===================== --> <xsl:template match="cbc:PayableAmount"> <cbc:GrandTotal> <xsl:copy-of select="@*"/> <xsl:apply-templates/> </cbc:GrandTotal> </xsl:template> <!-- ===================== --> <!-- 6. Add signature placeholder after <cbc:PayableAmount> --> <!-- ===================== --> <xsl:template match="cbc:PayableAmount"> <xsl:copy-of select="."/> <!-- Insert placeholder --> <cac:Signature> <cbc:ID>SIGN-PLACEHOLDER-2025</cbc:ID> <cbc:Note>Digital signature to be added</cbc:Note> </cac:Signature> </xsl:template> </xsl:stylesheet> |
How to test this
Using command line (if you have Saxon or xsltproc)
|
0 1 2 3 4 5 6 7 |
# Using xsltproc (libxslt) xsltproc edit-invoice.xsl invoice.xml > invoice-edited.xml |
Or using online tools (for quick testing):
- https://www.freeformatter.com/xslt-transformer.html
- https://www.utilities-online.info/xslt/
- Oxygen XML Editor (free trial) or VS Code with XSLT extension
Summary – Most common “edit XML” tasks with XSLT
| Task | Typical pattern / technique | Key instruction / tip |
|---|---|---|
| Add new element at specific position | Match preceding element → output copy + new element | Use xsl:copy-of + insert after |
| Change element text | <xsl:template match=”title”> → <xsl:value-of select=”new-value”/> | Use textContent in JS, xsl:value-of in XSLT |
| Change / add attribute | xsl:copy + xsl:attribute or setAttribute equivalent | <xsl:attribute name=”new”>value</xsl:attribute> |
| Remove element | Match it → output nothing | Most elegant way in XSLT |
| Rename element | Match old name → output new tag name + copy content | <xsl:element name=”NewName”> + `<xsl:copy-of select=”@* |
| Move node to different position | Copy it → remove original → paste copy | Two templates: one to copy, one to skip original |
| Conditional addition/removal | Use <xsl:if> or <xsl:choose> around copy | Very common for enrichment / filtering |
Would you like to continue with one of these next?
- Identity transform + small modifications (copy most, change few)
- Removing nodes conditionally (out-of-stock, sensitive data)
- Moving nodes (reordering elements)
- Adding namespaces / signatures (real GST/UBL patterns)
- Replacing whole subtrees (update entire sections)
- Debugging when changes don’t appear or XML breaks
Just tell me which direction you want to go next! 😊
