Search code examples
xslt-2.0

search and replace xml error using xslt2


Given the following input xml file, i need to search for values, and replace them in the input xml

the search for values are in the xslt, each row must be replaced by its equivalent row.

<TABLE NAME="TEST">
<DATA RECORDS="78">
<catalog>
   <book id="bk109">
      <description>2ος Όροφος</description>
   </book>
   <book id="bk110">
      <description>Microsoft's .NET initiative is explored in detail in this deep programmer's reference.</description>
   </book>
   <book id="bk111">
      <description>An anthology of HORROR stories about roaches, centipedes, scorpions  and other insects.</description>
   </book>
   <book id="bk112">
      <description>2ος όροφος</description>
   </book>
   <book id="bk113">
      <description>An anthology of horror stories about roaches, centipedes, scorpions  and other insects.</description>
   </book>
   <book id="bk114">
      <description>Microsoft's .NET initiative is explored in detail in this deep PROGRAMMER's reference.</description>
   </book>
   <book id="bk115">
      <description>An anthology of HORROR stories about roaches, centipedes, scorpions  and other insects.</description>
   </book>
   <book id="bk116">
      <description>An anthology of horror stories about roaches, centipedes, scorpions  and other insects. Beware, this must not be matched.</description>
   </book>
   <book id="bk114">
      <description>Microsoft's .NET initiative is explored in detail in this deep PROGRAMMER's reference. Beware, this must not be matched.</description>
   </book>
   </catalog>
</DATA>
</TABLE>

and the following xslt:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:functx="http://www.functx.com"
    exclude-result-prefixes="xs functx">

    <xsl:param name="search-text" as="xs:string">2ος Όροφος
2ος όροφος</xsl:param>

    <xsl:param name="replacement-text" as="xs:string">2ος όροφoς
2ος όροφoς</xsl:param>

    <xsl:param name="search-terms" as="xs:string*" select="tokenize($search-text, '\r?\n')"/>

    <xsl:param name="search-terms-is" as="xs:string*" select="for $term in $search-terms return concat('^', lower-case(functx:escape-for-regex($term)), '$')"/>

    <xsl:param name="replace-terms" as="xs:string*" select="tokenize($replacement-text, '\r?\n')"/>

    <xsl:include href="http://www.xsltfunctions.com/xsl/functx-1.0-nodoc-2007-01.xsl"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>


<xsl:template match="description[some $search-term in $search-terms-is satisfies matches(., $search-term, 'i')]">
    <xsl:copy>
        <xsl:variable name="matched-term" as="xs:string" select="$search-terms-is[matches(current(), ., 'i')]"/>
        <xsl:variable name="replacement" as="xs:string" select="$replace-terms[index-of($search-terms-is, $matched-term)]"/>
        <xsl:value-of
            select="$replacement"/>
    </xsl:copy>
</xsl:template>

</xsl:transform>

i get the errors.

How could the above be altered, so as to keep all the code the xslt has, but avoid the following error?

<?xml version="1.0" encoding="UTF-8"?><TABLE NAME="TEST">
<DATA RECORDS="78">
<catalog>
   <book id="bk109">
      Error on line 30 
  XTTE0570: A sequence of more than one item is not allowed as the value of variable
  $matched-term ("^2ος όροφος$", "^2ος όροφος$") 
  at xsl:apply-templates (#23)
     processing /TABLE/DATA[1]/catalog[1]/book[1]/description[1]
  at xsl:apply-templates (#23)
     processing /TABLE/DATA[1]/catalog[1]/book[1]
  at xsl:apply-templates (#23)
     processing /TABLE/DATA[1]/catalog[1]
  at xsl:apply-templates (#23)
     processing /TABLE/DATA[1]
  in built-in template rule

live here: http://xsltransform.net/pNvs5wd

Also i would appreciate an xslt, where the search and replace values could be loaded in external files. ie search.txt, replace.txt


Solution

  • The posted code assumes any search term is entered once and is the converted by the XSLT to lower case in <xsl:param name="search-terms-is" as="xs:string*" select="for $term in $search-terms return concat('^', lower-case(functx:escape-for-regex($term)), '$')"/>. Furthermore the matching of input data with the search terms in some $search-term in $search-terms-is satisfies matches(., $search-term, 'i') is done using the i flag to use case-insensitive matching. Your two terms 2ος Όροφος and 2ος όροφος differ only in the case of characters so they both result in a match of the input data. As the variable storing the match is type as xs:string expecting only a single match you get that error.

    To fix that you should either make sure your search terms contain a certain term only once or you need to remove the use of lower-case and the i flag.

    As for using text files with the search and replacement list, XSLT 2 and later supports the XPath 2 and later unparsed-text(file-uri, optional-encoding) function you can use as in

    <xsl:param name="searchFile" as="xs:string">search-terms.txt</xsl:param>
    <xsl:param name="search-text" as="xs:string" select="unparsed-text($searchFile)"/>
    

    to load the terms from a text file.