0
votes

I have this regexp and substitution patterns demo and need to use it within an xpath context with the fn:replace function,but I can't figure out how to write the replacement string correctly Is it possible ? my naive test was

replace ("dsfjkljsdfjlsjdfABCDdfsfsdff",
             "(\p{Lu})(\p{Lu}+)",
             "$1\L$2")

but it complains with FORX0004 : Invalid replacement string in replace() : \ character must be followed by \ or $

2
Can you explain in plain English which replacement you want to perform? Also consider to include the relevant samples in the question text. Note that within XSLT 2 and later you have user defined functions and xsl:analyze-string to "implement" anything with regular expressions the XPath 2 replace might not give you, so if you want to apply a string function to the part of the match I think you should look at that option. - Martin Honnen
the purpose is to detect sequence of contiguous UPPERCASE characters within a word and to transform it to Uppercase - Mystic Tm

2 Answers

1
votes

I think you want e.g.

<xsl:function name="mf:lower-case-match">
  <xsl:param name="input" as="xs:string"/>
  <xsl:param name="regex" as="xs:string"/>
  <xsl:analyze-string select="$input" regex="{$regex}">
    <xsl:matching-substring>
      <xsl:value-of select="concat(regex-group(1), lower-case(regex-group(2)))"/>
    </xsl:matching-substring>
    <xsl:non-matching-substring>
      <xsl:value-of select="."/>
    </xsl:non-matching-substring>
  </xsl:analyze-string>
</xsl:function>

mf:lower-case-match("dsfjkljsdfjlsjdfABCDdfsfsdff", "(\p{Lu})(\p{Lu}+)")

or, to use the as="xs:string" as the declared function type:

<xsl:function name="mf:lower-case-match" as="xs:string">
  <xsl:param name="input" as="xs:string"/>
  <xsl:param name="regex" as="xs:string"/>
  <xsl:value-of>
      <xsl:analyze-string select="$input" regex="{$regex}">
        <xsl:matching-substring>
          <xsl:value-of select="concat(regex-group(1), lower-case(regex-group(2)))"/>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
          <xsl:value-of select="."/>
        </xsl:non-matching-substring>
      </xsl:analyze-string>          
  </xsl:value-of>
</xsl:function>

You need to declare a namespace for any user-defined function e.g. xmlns:mf="http://example.com/mf" on the xsl:stylesheet or xsl:transform root.

In XSLT 3 you could also simply push the result of the analyze-string function through a mode that then performs any transformation on the groups you want:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="text">
      <xsl:copy>
          <xsl:apply-templates select="analyze-string(., '(\p{Lu})(\p{Lu}+)')" mode="lower-case"/>
      </xsl:copy>
  </xsl:template>
  
  <xsl:template match="*:group[@nr = 2]" mode="lower-case">
      <xsl:value-of select="lower-case(.)"/>
  </xsl:template>
  
</xsl:stylesheet>
0
votes

I don't think \L regex property is supported with XPath. @Martin Honnen's answer is probably the best, but here's a full XPath 2.0 solution :

With :

dsfjkljsdfjlsjdfABCDdfsfsdff

XPath :

replace(replace("dsfjkljsdfjlsjdfABCDdfsfsdff","(\p{Lu})(\p{Lu}+)","$1___$2___"),"_{3}.+_{3}",lower-case(substring-before(substring-after(replace("dsfjkljsdfjlsjdfABCDdfsfsdff","(\p{Lu})(\p{Lu}+)","$1___$2___"),"___"),"___")))

Description :

P1 : We add ___ to identify the lower-case part with :

replace("dsfjkljsdfjlsjdfABCDdfsfsdff","(\p{Lu})(\p{Lu}+)","$1___$2___")

P2 : We generate the lower case part with :

lower-case(substring-before(substring-after(resultofP1,"___"),"___"))

We join the two preceding expressions with :

replace(resultofP1,"_{3}.+_{3}",resultofP2)

Output :

dsfjkljsdfjlsjdfAbcddfsfsdff