Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 9165389
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 17, 20262026-06-17T14:53:53+00:00 2026-06-17T14:53:53+00:00

I’d like to extract all references to the document root from an XPath expression

  • 0

I’d like to extract all references to the document root from an XPath expression and inject a custom root after them.

I’m implementing a smallish part of validation (or rather fixing a bug) of an XML instance document created based on some schema language. This language provides means of specifying self contained chunks of XML. Each such chunk is defined within a separate file and specifies the XML element hierarchy. Each such hierarchy has one or more root elements belonging to the same document root, much like the invisible document root of any XML document.

These files are however not aware of the fact that, what they specify, is only a part of a larger system. This larger system is actually another XML document (having another document root) with a single top-level XML element, which contains all the root elements defined by any number of such schema language files.

Any node in the XML hierarchy may be constrained with an XPath expression which must evaluate to true in order for the element to be considered valid during validation. Herein lies the root of my problem. These XPath expressions may contain absolute location paths, which reference the document root of a single XML chunk and not the document root of the system. Consider the following XML instance:

<data xmlns="system:uri">
    <root-one xmlns="root-one:uri">
        <items>
            <item>
                <group>base</group>
                <class>person</person>
                <name>John Smith</name>
                <description>valid entry</description>
            </item>
            <item>
                <group>base</group>
                <class>animal</person>
                <name>Dog</name>
                <description>invalid entry</description>
            </item>
        </items>
        <item-classes>
            <item-class>                
                <class>person</class>
                <group>base</group>
            </item-class>
        </item-classes>
    </root-one>
    <root-two xmlns="root-two:uri">
        <!-- obscured content -->
    </root-two>
</data>

{system:uri}data represents the system, {root-one:uri}root-one and {root-two:uri}root-two are two chunks of XML, each defined within it’s own schema language file. Let’s say that each root-one/items/item instance must fulfill the following XPath condition, defined within the schema language file (don’t mind the current(), it works the same as the one in XSLT, referring to one of the item instances):

context: /root-one/items/item
assert: group=/root-one/item-classes/item-class[class=current()/class]/group

which should actually be

context: /data/root-one/items/item
assert: group=/data/root-one/item-classes/item-class[class=current()/class]/group

How do I get all references to the document root (/) in any XPath expression and inject them with the correct root? I have no control over how these expressions are formed, so they may come in any shape and size, as long as they satisfy XPath 1.0 syntax, but I have to make them evaluate properly.

I’m currently thinking of writing some sort of a tokenizer in java to handle this, but I would rather not go into it if there’s a simpler solution. The expressions are evaluated during a Schematron XSLT transformation within the context of the system document, so if I could somehow achieve path fixing using XSLT it would be perfect. I’m ready to accept any pointers that could lead me to a solution however.

Edit01

This is what an example file which contains the XPath expressions looks like (off the top of my head). I wish to transform the content of the @test attribute. Value of @context attribute is trivial to change since it always has a similar structure.

<?xml version="1.0" encoding="utf-8"?>
<iso:schema    xmlns="http://purl.oclc.org/dsdl/schematron" 
           xmlns:iso="http://purl.oclc.org/dsdl/schematron" 
           xmlns:sch="http://www.ascc.net/xml/schematron"
           xmlns:tl="toplevel:uri"
           xmlns:r1="root-one:uri"
           xmlns:r2="root-two:uri">

  <iso:ns prefix="tl" uri="toplevel:uri" />
  <iso:ns prefix="r1" uri="root-one:uri" />
  <iso:ns prefix="r2" uri="root-two:uri" />

  <iso:pattern>
    <iso:rule context="/r1:root-one/r1:items/r1:item">
      <iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>
    </iso:rule>
  </iso:pattern>

</iso:schema>

Please note that the value of @test attribute can be any valid XPath 1.0 expression. I’d like to se a generic solution which can find any document root (‘/’) defined anywhere within the expression and inject it with a custom root element. The actual file may contain any number of iso:pattern elements, iso:rule elements, etc.

Edit02

For the example above the wanted result is the follwing iso:assert element:

<iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>

Edit03

In response to How do you decide that /r1:root-one/ must be preceded by ‘/tl:data’ ? Could you, please describe the rules? – Dimitre Novatchev

/tl:data represents a root element of a document which is created by combining multiple other XML documents into a single one. The content of those documents in appended to this root element as children. r1:root-one becomes one of such children. The XPath constraints, which are a part of schema definition which describe what the element structure of r1:root-one looks like, are designed to work only in the context of this sub- XML document. When the sub- XML document gets appended to the “parent” document they lose meaning if absolute paths are present within the expression. So if the expression contains /r1:root-one this will have no meaning in the new document (there is no root-one root element within it, tl:data is the only root). I’d like to find all such cases (/r1:root-one/) and transform them (into /tl:data/r1:root-one/) so the expressions work in the context of the new document.

It is hard to specify the exact rules. Each occurrence of “/” which appears at the begining of a path (and therefore references the document root of a sub- XML document) should be replaced with “/tl:data/” so it now references the document root of the newly created document.

Edit04

As indicated in the text above, the solution should work for any XPath expression imaginable. Additional examples (imaginary elments from r1 namespace are made up – this sounded better inside my head):

<iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1" />
<iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group" />

should become

<iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1" />
<iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=/tl:data/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group" />

Edit05

I now have the option to switch to an XSLT 2.0 processor. So I’ll accept XSLT 2.0 solutions.

In fact if someone could provide me with an XSLT regular expression which would match / sign which represents the document root within an XPath 1.0 expression, this would solve my problem (I would use the replace() function). I’ve been looking through XPath 1.0 grammar but have not come with anything useful yet.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-17T14:53:54+00:00Added an answer on June 17, 2026 at 2:53 pm

    After examining XPath 1.0 grammar/spec and switching to XSLT2.0 in order to gain regex support, I’ve come up with the following monstrosity.

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:iso="http://purl.oclc.org/dsdl/schematron"
     xmlns:def="http://purl.oclc.org/dsdl/schematron">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:template match="node()|@*">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="iso:assert">
        <xsl:copy>
    
            <xsl:variable name="no-double-slash">
              <!-- need to ignore double slashes since they interfere later -->  
              <xsl:value-of select="replace(@test,'//','unlikelycharseq_double_slash')" />
            </xsl:variable>        
            <xsl:variable name="no-asterisk">
              <!-- need to ignore '*/' which is preceded by and|or|mod|div since that's not mul operator -->  
              <xsl:value-of select="replace($no-double-slash,'([^a-zA-Z0-9_\-](and|or|mod|div)\s*)\*\s*/',concat('$1','unlikelycharseq_asterisk_slash'))" />
            </xsl:variable>
            <xsl:variable name="escaped1">
              <!-- any slash at line start or after an operator (except mul) needs to be replaced, '-' needs to be preceded by whitespace if it represents an operator -->  
              <xsl:value-of select="replace($no-asterisk,'^\s*/|([=+&lt;&gt;\[\(]\s*|\s+\-\s*|[^a-zA-Z0-9_\-](and|or|mod|div)\s*)/',concat('$1','/tl:data/'))" />
            </xsl:variable>
            <xsl:variable name="escaped2">
              <!-- any '*' not preceded by an operator, '@', '::', '(', '[' or ','  is a mul operator, so the following slash needs to be replaced -->  
              <xsl:value-of select="replace($escaped1,'((\s+[^\-]|[^@:\(\[,/|+=&gt;&lt;*])\s*\*\s*)/',concat('$1','/tl:data/'))" />
            </xsl:variable>  
            <xsl:variable name="with-asterisk">
              <!-- restore '*/' which we needed to ignore for the above regexes to work  -->  
              <xsl:value-of select="replace($escaped2,'unlikelycharseq_asterisk_slash','*/')" />
            </xsl:variable>        
            <xsl:variable name="fixed-path">
              <!-- restore '//' which we needed to ignore for the above regexes to work -->  
              <xsl:value-of select="replace($with-asterisk,'unlikelycharseq_double_slash','//')" />
            </xsl:variable>      
    
          <xsl:attribute name="test">
            <xsl:value-of select="$fixed-path" />
          </xsl:attribute>
    
          <xsl:comment>
            <xsl:value-of select="@test" />
          </xsl:comment>
      </xsl:copy>
     </xsl:template>
    </xsl:stylesheet>
    

    It implements some rules that must be fulfilled in order for ‘/’ to represent the document root and not a path separator:

    1. appears at line start
    2. appears after an operator
    3. appears after [, (, etc.

    A couple of XPath tokens need special treatment when processing with regex and whitespace must also be taken into account. Characters ‘*’ and ‘-‘ might not represent operators at all. So far this worked for all my test cases but, since I’m relying on grammar instead of experience with XPath in order to form the expressions, I might have missed something.

    These regex expressions demonstrate quite a few capabilities of XML Schema/XPath regex flavor. Multiple replace runs are required since some advanced features are not supported in it. The most notable being lookaround.

    If someone gives me a better solution than this spaghetti XSLT I’ll gladly accept it. Note that this might not be the best and not even the only solution to my problem.

    When this transformation is applied to

    <iso:schema    xmlns="http://purl.oclc.org/dsdl/schematron"
               xmlns:iso="http://purl.oclc.org/dsdl/schematron"
               xmlns:sch="http://www.ascc.net/xml/schematron"
               xmlns:tl="toplevel:uri"
               xmlns:r1="root-one:uri"
               xmlns:r2="root-two:uri">
    
      <iso:ns prefix="tl" uri="toplevel:uri" />
      <iso:ns prefix="r1" uri="root-one:uri" />
      <iso:ns prefix="r2" uri="root-two:uri" />
    
      <iso:pattern>
        <iso:rule context="/r1:root-one/r1:items/r1:item">
          <iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>
          <iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1" />
          <iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group" />
          <iso:assert test="../type[. * /root-one/imaginary-constants/imaginary-constant1 > 10]"/>
          <iso:assert test="../type[. mod /root-one/imaginary-constants/imaginary-constant1 = 1]"/>
          <iso:assert test="   /root-one/imaginary-constants/imaginary-constant1"/>
          <iso:assert test="../type[. mod /root-one/imaginary-constants/imaginary-constant1 = 1]"/>
          <iso:assert test="../group= /root-one/item-classes/item-class[name=current()/name]/group and ../class=/root-one/item-classes/item-class[name=current()/name]/class"/>
          <iso:assert test="../type[. = /root-one/imaginary-constants/imaginary-constant1]"/>
          <iso:assert test="../type[.*/root-one/imaginary-constants/imaginary-constant1 = 1]"/>
          <iso:assert test="../type[/root-one/imaginary-constants/imaginary-constant1 mod ../../*/type]"/>
          <iso:assert test="../type[/root-one/imaginary-constants/imaginary-constant1 mod ../multiminus---/type > 1]"/>
          <iso:assert test="../type[../multiminus---*/root-one/imaginary-constants/imaginary-constant1 > 1]"/>
          <iso:assert test="//container/*/type[. &gt; 5]"/>
        </iso:rule>
      </iso:pattern>
    </iso:schema>
    

    it results in this (paths fixed as expected)

    <iso:schema xmlns="http://purl.oclc.org/dsdl/schematron"
                xmlns:iso="http://purl.oclc.org/dsdl/schematron"
                xmlns:sch="http://www.ascc.net/xml/schematron"
                xmlns:tl="toplevel:uri"
                xmlns:r1="root-one:uri"
                xmlns:r2="root-two:uri">
       <iso:ns prefix="tl" uri="toplevel:uri"/>
       <iso:ns prefix="r1" uri="root-one:uri"/>
       <iso:ns prefix="r2" uri="root-two:uri"/>
       <iso:pattern>
          <iso:rule context="/r1:root-one/r1:items/r1:item">
             <iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group"><!--r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group--></iso:assert>
             <iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1"><!--r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1--></iso:assert>
             <iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=/tl:data/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group"><!--r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group--></iso:assert>
             <iso:assert test="../type[. * /tl:data/root-one/imaginary-constants/imaginary-constant1 &gt; 10]"><!--../type[. * /root-one/imaginary-constants/imaginary-constant1 > 10]--></iso:assert>
             <iso:assert test="../type[. mod /tl:data/root-one/imaginary-constants/imaginary-constant1 = 1]"><!--../type[. mod /root-one/imaginary-constants/imaginary-constant1 = 1]--></iso:assert>
             <iso:assert test="/tl:data/root-one/imaginary-constants/imaginary-constant1"><!--   /root-one/imaginary-constants/imaginary-constant1--></iso:assert>
             <iso:assert test="../type[. mod /tl:data/root-one/imaginary-constants/imaginary-constant1 = 1]"><!--../type[. mod /root-one/imaginary-constants/imaginary-constant1 = 1]--></iso:assert>
             <iso:assert test="../group= /tl:data/root-one/item-classes/item-class[name=current()/name]/group and ../class=/tl:data/root-one/item-classes/item-class[name=current()/name]/class"><!--../group= /root-one/item-classes/item-class[name=current()/name]/group and ../class=/root-one/item-classes/item-class[name=current()/name]/class--></iso:assert>
             <iso:assert test="../type[. = /tl:data/root-one/imaginary-constants/imaginary-constant1]"><!--../type[. = /root-one/imaginary-constants/imaginary-constant1]--></iso:assert>
             <iso:assert test="../type[.*/tl:data/root-one/imaginary-constants/imaginary-constant1 = 1]"><!--../type[.*/root-one/imaginary-constants/imaginary-constant1 = 1]--></iso:assert>
             <iso:assert test="../type[/tl:data/root-one/imaginary-constants/imaginary-constant1 mod ../../*/type]"><!--../type[/root-one/imaginary-constants/imaginary-constant1 mod ../../*/type]--></iso:assert>
             <iso:assert test="../type[/tl:data/root-one/imaginary-constants/imaginary-constant1 mod ../multiminus---/type &gt; 1]"><!--../type[/root-one/imaginary-constants/imaginary-constant1 mod ../multiminus- - -/type > 1]--></iso:assert>
             <iso:assert test="../type[../multiminus---*/tl:data/root-one/imaginary-constants/imaginary-constant1 &gt; 1]"><!--../type[../multiminus- - -*/root-one/imaginary-constants/imaginary-constant1 > 1]--></iso:assert>
             <iso:assert test="//container/*/type[. &gt; 5]"><!--//container/*/type[. > 5]--></iso:assert>
          </iso:rule>
       </iso:pattern>
    </iso:schema>
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

For some reason, after submitting a string like this Jack’s Spindle from a text
I have a string like this: La Torre Eiffel paragonata all&#8217;Everest What PHP function
I have a text area in my form which accepts all possible characters from
Let's say I'm outputting a post title and in our database, it's Hello Y&#8217;all
link Im having trouble converting the html entites into html characters, (&# 8217;) i
I've got a string that has curly quotes in it. I'd like to replace
I would like to run a str_replace or preg_replace which looks for certain words
I am trying to render a haml file in a javascript response like so:
I'm parsing an RSS feed that has an &#8217; in it. SimpleXML turns this
I'm trying to convert HTML to plain text. I get many &\#8217; &\#8220; etc.

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.