XSLT - Regular Expression Processing with <xsl:analyze-string> in XSLT
<xsl:analyze-string> is an XSLT 2.0 feature used for advanced text processing with regular expressions. It allows an XSLT stylesheet to examine a string, identify portions that match a specified pattern, and process both the matching and non-matching portions separately. The W3C specification describes it as an instruction intended for string processing that is more complex than what can conveniently be handled with functions such as matches(), replace(), and tokenize(). (W3C)
This feature is particularly useful when XML contains unstructured or semi-structured text, such as product descriptions, addresses, telephone numbers, email addresses, dates, identifiers, log messages, or text containing special patterns. Instead of processing the entire string as one value, xsl:analyze-string lets you distinguish the parts that satisfy a regular expression from the parts that do not.
1. What Is Regular Expression Processing?
A regular expression, commonly called a regex, is a pattern used to identify particular arrangements of characters within text.
For example, consider this text:
Order ID: ORD-2026-125
Suppose you want to identify the order number:
ORD-2026-125
A regular expression can describe this pattern:
ORD-[0-9]{4}-[0-9]+
The pattern means:
-
ORD-must occur literally. -
[0-9]{4}represents exactly four digits. -
-represents a hyphen. -
[0-9]+represents one or more digits.
XSLT can use such a pattern to locate the matching portion of the string.
XSLT 2.0 introduced xsl:analyze-string specifically for this kind of more advanced string analysis. (W3C)
2. Basic Syntax of xsl:analyze-string
The basic structure is:
<xsl:analyze-string select="expression"
regex="regular-expression"
flags="optional-flags">
<xsl:matching-substring>
...
</xsl:matching-substring>
<xsl:non-matching-substring>
...
</xsl:non-matching-substring>
</xsl:analyze-string>
The three important parts are:
select
The select attribute specifies the string that should be analyzed.
select="description"
The expression is evaluated and converted to a string when necessary. (W3C)
regex
The regex attribute contains the regular expression used to identify matching text.
regex="[0-9]+"
This pattern identifies one or more consecutive digits.
flags
The optional flags attribute changes how the regular expression is interpreted.
For example:
flags="i"
makes matching case-insensitive.
The W3C specification defines flags that control aspects such as case sensitivity, multiline processing, and dot-all behavior. (W3C)
3. xsl:matching-substring
The xsl:matching-substring element contains the instructions that should be executed for portions of the input string that match the regular expression.
For example:
<xsl:analyze-string select="description"
regex="[0-9]+">
<xsl:matching-substring>
<number>
<xsl:value-of select="."/>
</number>
</xsl:matching-substring>
</xsl:analyze-string>
If the input is:
<description>Product 125 is available</description>
the matching substring is:
125
The output could therefore contain:
<number>125</number>
The . inside xsl:matching-substring represents the current matching substring.
4. xsl:non-matching-substring
The xsl:non-matching-substring element processes the portions of the input that do not match the regular expression.
For example:
<xsl:analyze-string select="description"
regex="[0-9]+">
<xsl:matching-substring>
<number>
<xsl:value-of select="."/>
</number>
</xsl:matching-substring>
<xsl:non-matching-substring>
<text>
<xsl:value-of select="."/>
</text>
</xsl:non-matching-substring>
</xsl:analyze-string>
For:
Product 125 is available
the transformation can distinguish:
Product
125
and:
is available
This separation is one of the most useful aspects of xsl:analyze-string.
5. Complete Example
Consider the following XML:
<products>
<product>
<description>Product ABC costs 250 dollars.</description>
</product>
</products>
Suppose the requirement is to identify the numeric value and wrap it inside an HTML <strong> element.
An XSLT stylesheet could be:
<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>
<xsl:apply-templates select="products/product"/>
</body>
</html>
</xsl:template>
<xsl:template match="product">
<p>
<xsl:analyze-string
select="description"
regex="[0-9]+">
<xsl:matching-substring>
<strong>
<xsl:value-of select="."/>
</strong>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</p>
</xsl:template>
</xsl:stylesheet>
The resulting HTML can be conceptually represented as:
<p>Product ABC costs <strong>250</strong> dollars.</p>
The regular expression identifies 250, while the non-matching portions are copied as normal text.
6. Matching Email Addresses
One practical use of regular expressions is identifying email addresses embedded in XML text.
For example:
<message>
Contact [email protected] for assistance.
</message>
A simplified email pattern could be:
[\w.-]+@[\w.-]+\.[A-Za-z]+
The XSLT can then process the email address separately:
<xsl:analyze-string
select="message"
regex="[\w.-]+@[\w.-]+\.[A-Za-z]+">
<xsl:matching-substring>
<a href="mailto:{.}">
<xsl:value-of select="."/>
</a>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
The result can turn the detected email address into a clickable link.
This demonstrates an important advantage of xsl:analyze-string: the stylesheet does not need to know exactly where the email address occurs in the text.
7. Identifying Telephone Numbers
Suppose the XML contains:
<contact>
Call us at 9876543210 for more information.
</contact>
A simple pattern for a ten-digit number is:
[0-9]{10}
The XSLT could process it as:
<xsl:analyze-string
select="contact"
regex="[0-9]{10}">
<xsl:matching-substring>
<phone>
<xsl:value-of select="."/>
</phone>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
This can be useful when telephone numbers are embedded inside larger blocks of text.
8. Extracting Dates
Regular expressions can also identify dates.
For example:
2026-08-11
A basic pattern is:
[0-9]{4}-[0-9]{2}-[0-9]{2}
The XSLT can identify the date and transform it differently from the surrounding text:
<xsl:analyze-string
select="text"
regex="[0-9]{4}-[0-9]{2}-[0-9]{2}">
<xsl:matching-substring>
<date>
<xsl:value-of select="."/>
</date>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
This can be useful when converting textual documents into structured XML.
9. Using Capturing Groups
Regular expressions can contain groups that allow different portions of a match to be identified separately.
Consider:
ORD-2026-125
A pattern could be:
(ORD)-([0-9]{4})-([0-9]+)
The groups identify:
ORD
2026
125
Within xsl:matching-substring, captured groups can be accessed using regex-group().
For example:
<xsl:analyze-string
select="order-id"
regex="(ORD)-([0-9]{4})-([0-9]+)">
<xsl:matching-substring>
<order>
<prefix>
<xsl:value-of select="regex-group(1)"/>
</prefix>
<year>
<xsl:value-of select="regex-group(2)"/>
</year>
<number>
<xsl:value-of select="regex-group(3)"/>
</number>
</order>
</xsl:matching-substring>
</xsl:analyze-string>
For:
ORD-2026-125
the result can be:
<order>
<prefix>ORD</prefix>
<year>2026</year>
<number>125</number>
</order>
This is particularly useful when an identifier contains several meaningful components.
10. Understanding regex-group()
regex-group() is used to retrieve the value captured by a particular regular-expression group.
For example:
regex="([A-Z]+)-([0-9]+)"
contains two groups.
The first group can be retrieved with:
regex-group(1)
and the second group with:
regex-group(2)
For:
ABC-125
the results are:
regex-group(1) = ABC
regex-group(2) = 125
This allows a transformation to convert unstructured identifiers into structured XML elements.
11. Using Regular Expression Flags
The flags attribute can modify how matching occurs.
For example:
flags="i"
means that the matching is case-insensitive.
Suppose the input is:
Error: CONNECTION FAILED
and the expression is:
regex="error"
flags="i"
The expression can match Error, ERROR, error, or other case variations.
Other flags can affect multiline processing and the behavior of the dot character. The exact supported flags depend on the regular-expression rules defined for the XSLT version and processor. (W3C)
12. Difference Between matches(), replace(), tokenize(), and xsl:analyze-string
XSLT 2.0 provides several regular-expression capabilities, but they have different purposes. The W3C specification identifies matches(), replace(), and tokenize() as regular-expression functions, while xsl:analyze-string provides more sophisticated processing. (W3C)
matches()
Use matches() when you only need to determine whether a pattern exists.
<xsl:value-of select="matches('Order 125', '[0-9]+')"/>
The result is:
true
replace()
Use replace() when you want to replace matching text.
<xsl:value-of select="replace('Order 125', '[0-9]+', 'XXX')"/>
The result is:
Order XXX
tokenize()
Use tokenize() when you want to split a string based on a regular expression.
<xsl:for-each select="tokenize('red,green,blue', ',')">
<item>
<xsl:value-of select="."/>
</item>
</xsl:for-each>
xsl:analyze-string
Use xsl:analyze-string when you need to process matching and non-matching portions differently.
For example:
<xsl:matching-substring>
...
</xsl:matching-substring>
<xsl:non-matching-substring>
...
</xsl:non-matching-substring>
Therefore, xsl:analyze-string is particularly useful when a transformation needs to preserve surrounding text while applying special processing to specific patterns.
13. Important Difference from Ordinary XPath String Functions
Traditional string functions are generally designed to perform operations such as:
substring()
substring-before()
substring-after()
contains()
These are useful for straightforward string manipulation.
Regular expressions are more suitable when the pattern is variable or structurally complex.
For example, finding a fixed word can be simple:
contains($text, 'error')
But finding different error identifiers such as:
ERR-101
ERR-205
ERR-500
can be handled more systematically with:
ERR-[0-9]+
This makes regular expressions valuable for pattern-based text processing.
14. Processing Multiple Matches
xsl:analyze-string processes the non-overlapping portions of the input that match the supplied regular expression. The specification describes the processing as starting from the beginning of the string and selecting matches according to their position. (W3C)
Consider:
Product 125 costs 250 dollars.
with:
regex="[0-9]+"
There are two matches:
125
250
The matching-substring instruction is therefore applied to each matching portion.
For example:
<xsl:analyze-string
select="description"
regex="[0-9]+">
<xsl:matching-substring>
<number>
<xsl:value-of select="."/>
</number>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
This allows every number in the input to be handled consistently.
15. Important Rule About Zero-Length Matches
One important restriction is that the regular expression used with xsl:analyze-string must not match a zero-length string. The W3C specification identifies this as a dynamic error. (W3C)
For example, a pattern that can successfully match an empty string should not be used directly with xsl:analyze-string.
This restriction exists because processing zero-length matches repeatedly could create ambiguity about how the transformation should progress through the input.
Therefore, when designing a regular expression for xsl:analyze-string, ensure that it identifies at least one character.
16. Curly Braces in Regular Expressions
There is an important XSLT syntax issue when regular expressions contain quantifiers.
A regular expression might normally contain:
[0-9]{4}
However, XSLT uses { and } for attribute value templates. Therefore, curly braces used literally within the regex attribute may need to be escaped by doubling them.
For example:
regex="[0-9]{{4}}"
represents a pattern matching four digits.
The W3C specification specifically discusses this issue because regular expressions commonly use curly braces for quantifiers. (W3C)
This is an important detail for beginners because:
regex="[0-9]{4}"
and:
regex="[0-9]{{4}}"
do not have the same meaning when interpreted as an XSLT attribute.
17. Practical Applications
xsl:analyze-string can be used in many XML transformation scenarios.
Document processing
It can identify headings, identifiers, dates, or special markers inside text.
Data cleaning
It can identify unwanted characters or patterns and transform them into a standardized form.
Report generation
Numbers, codes, and special keywords can be highlighted in generated HTML reports.
Log processing
Error codes and timestamps can be identified from log messages.
XML-to-HTML transformation
URLs, email addresses, numbers, and other patterns can be converted into HTML elements.
Identifier extraction
Composite identifiers can be separated into meaningful components.
Text annotation
Specific words or patterns can be wrapped in XML or HTML elements while preserving the original surrounding text.
18. Advantages of xsl:analyze-string
The main advantages are:
-
Pattern-based processing
Complex text patterns can be identified instead of relying only on exact string comparisons. -
Separate match handling
Matching and non-matching text can be processed independently. -
Multiple-match support
A single input string can contain multiple matching sections. -
Capturing groups
Parts of a match can be extracted usingregex-group(). -
Flexible text transformation
Identified patterns can be converted into elements, attributes, links, or other output structures. -
Useful for semi-structured text
It is particularly valuable when important information is embedded inside ordinary text.
19. Limitations and Considerations
Regular expressions should not be treated as a replacement for XML structure.
If information is already represented properly in XML:
<customer>
<email>[email protected]</email>
</customer>
it is generally better to access:
customer/email
rather than searching the entire document with a regular expression.
Regular expressions are most useful when the information is embedded inside text:
<description>
Contact [email protected] for order 125.
</description>
Here, the email address and order number are not separate XML elements, so pattern matching becomes useful.
Another consideration is that regular expressions can become difficult to understand when they are excessively complex. A clear XML structure is normally preferable to encoding complicated data inside text.
20. Summary
xsl:analyze-string is a powerful XSLT 2.0 instruction for processing text with regular expressions. It accepts an input string through select, defines a pattern through regex, and optionally modifies matching behavior through flags. Its two principal child instructions, xsl:matching-substring and xsl:non-matching-substring, allow matched and unmatched portions of the input to be processed independently. (W3C)
The feature becomes especially useful when XML contains semi-structured text containing email addresses, telephone numbers, dates, product codes, identifiers, numbers, or other recognizable patterns. Capturing groups and regex-group() make it possible to extract individual components from complex matches.
A useful way to remember the concept is:
Input Text
|
v
Regular Expression
|
v
Find Matching Portions
|
+----------------------+
| |
v v
Matching Text Non-Matching Text
| |
v v
Special Processing Normal Processing
| |
+----------+-----------+
|
v
Final Output
In short, xsl:analyze-string allows XSLT to look inside ordinary text, recognize patterns using regular expressions, and transform the matching and non-matching portions in different ways. This makes it an important technique for advanced text transformation in XSLT 2.0 and later.