Using fn:parse-xml to Parse Escaped XML in XPath

The fn:parse-xml() function, introduced in XPath and XQuery 3.0, allows developers to dynamically convert serialized or escaped XML text strings into fully queryable XML node trees directly inside an XPath expression. This article explains the mechanics of how fn:parse-xml() operates on escaped XML strings, demonstrates how to chain path expressions onto its result, and highlights key considerations for error handling and document structure.

The Role of fn:parse-xml()

In many data processing scenarios, XML payloads are stored as escaped strings within other XML documents (such as CDATA sections or entity-encoded text like <root>...</root>) or passed as string parameters. Standard XPath navigation cannot traverse these inner structures because the host XML parser treats them purely as text nodes.

The fn:parse-xml() function bridges this gap with the following signature:

fn:parse-xml($arg as xs:string?) as document-node()?

It takes a single string argument, parses it according to the XML 1.0 rules for well-formedness, and returns a new document-node().

How fn:parse-xml() Processes Escaped Strings

When evaluating an escaped XML string directly within an XPath expression, the process occurs in three distinct phases:

  1. Entity Decoding by the Host Parser:
    Before the XPath expression evaluates the function argument, the host XML processor resolves entity references (e.g., converting &lt; to < and &amp; to &). As a result, the string passed into fn:parse-xml() is a raw XML markup string.

  2. In-Memory Tree Construction:
    The fn:parse-xml() function invokes a secondary XML parser on the string at runtime. If the string is well-formed XML, the parser instantiates a new document node hierarchy entirely in memory.

  3. Downstream XPath Navigation:
    Because the return type is a document-node(), you can append additional location path steps directly to the function call to query the newly parsed elements.

Example Walkthrough

Consider the following XML document where the <payload> element contains an escaped XML string:

<response>
    <status>200</status>
    <payload>&lt;order id="101"&gt;&lt;item sku="A-55"&gt;Widget&lt;/item&gt;&lt;/order&gt;</payload>
</response>

To extract the SKU attribute directly using a single XPath 3.0 expression:

fn:parse-xml(/response/payload)/order/item/@sku

Execution Breakdown:

Key Considerations