• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 410
  • Last Modified:

string replace using XSLT

I have my xml as

<root>

<case id=1>
<child>
<action querystring="p1=test;p2=test1" id=1 name="blah" />
<action querystring="p1=#clue1#;p2=test1" id=2 name="blah" />
</child>
</case>

</root>

if query string contains something like that as in action with id=2

querystring="p1=#Pass1:clue1#;p2=test1" id=2 name="blah"

so #clue1" is actually in other file

which looks like

<root>

<region id="pass1">
<parameter id="clue1" value="test" />
</region>

</root>

so i have to replave pass1:clue1 with test.

thanks




0
praneetha
Asked:
praneetha
  • 7
  • 5
1 Solution
 
Geert BormansCommented:
Hi praneetha,

just to be sure, I give you the input files I used (normalised)

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <case id="1">
        <child>
        <action querystring="p1=test;p2=test1" id="1" name="blah" />
            <action querystring="p1=#pass1:clue1#;p2=test1" id="2" name="blah" />
        </child>
    </case>
</root>

region.xml
-----------
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <region id="pass1">
        <parameter id="clue1" value="test" />
    </region>
</root>

this XSLT does what I think you want

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="node()">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
   
    <xsl:template match="action">
        <xsl:copy>
            <xsl:copy-of select="@*[not(name() = 'querystring')]"/>
            <xsl:choose>
                <xsl:when test="contains(@querystring, '#')">
                    <xsl:variable name="pre" select="substring-before(@querystring, '#')"/>
                    <xsl:variable name="post" select="substring-after(substring-after(@querystring, '#'), '#')"/>
                    <xsl:variable name="replace" select="substring-before(substring-after(@querystring, '#'), '#')"/>
                    <xsl:variable name="arg1" select="substring-before($replace, ':')"/>
                    <xsl:variable name="arg2" select="substring-after($replace, ':')"/>
                    <xsl:attribute name="querystring">
                        <xsl:value-of select="$pre"/>
                        <xsl:value-of select="document('region.xml')//region[@id=$arg1]/parameter[@id=$arg2]/@value"/>
                        <xsl:value-of select="$post"/>
                    </xsl:attribute>
                </xsl:when>
                <xsl:otherwise><xsl:copy-of select="@querystring"/></xsl:otherwise>
            </xsl:choose>
           
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
 </xsl:stylesheet>


Cheers!
0
 
Geert BormansCommented:
praneetha,

the template rule for node()
simply does an identity copy
this works for all elements except for the action element, which has its more specific template

for action I copy all the attributes except 'querystring'

if @querystring doesn't contain a '#' I also copy it

otherwise, I take the thing between the # and the #
split it based on the ':' and do a lookup in the other document

If you need a somewhat different logic, I will be happy to help

cheers

Geert
0
 
praneethaAuthor Commented:
thanks. will try it and let you know.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
praneethaAuthor Commented:
i think its working fine but fialing in cases where querystring can contain more than one #

like below

    <action querystring="p1=#pass1:clue1#;p2=#Pass2:Clue9#" id="2" name="blah" />

it works for pass1:clue1 and doesnt do pass2:clue9

i shoudl have mentioned this to you before.

me trying to fix it so that i cna learn too.

what does it mean when its precded by @ like @queryString. is it the real attribute from XML?

thanks for your help
0
 
Geert BormansCommented:
well, if you need to make multiple replacements of #....# sequences,
you need recursive processing of the attribute

I will develop an example in a minute...
you will see it is a bit more complex

@queryString indeed means the XML attribute queryString
0
 
Geert BormansCommented:
here is the XML I use

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <case id="1">
        <child>
        <action querystring="p1=test;p2=test1" id="1" name="blah" />
            <action querystring="p1=#pass1:clue1#;p2=#pass2:clue9#"  id="2" name="blah" />
        </child>
    </case>
</root>

NOTE that I lowercased the attributes in my example, it is all case sensitive, so make sure it is correct

this is region.xml
--------------------
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <region id="pass1">
        <parameter id="clue1" value="test" />
    </region>
    <region id="pass2">
        <parameter id="clue9" value="test5" />
    </region>
</root>

and here is the XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="node()">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
   
    <xsl:template match="action">
        <xsl:copy>
            <xsl:copy-of select="@*[not(name() = 'querystring')]"/>
            <xsl:choose>
                <xsl:when test="contains(@querystring, '#')">
                    <xsl:attribute name="querystring">
                        <xsl:call-template name="processQS">
                            <xsl:with-param name="qs" select="@querystring"/>
                        </xsl:call-template>
                    </xsl:attribute>
                </xsl:when>
                <xsl:otherwise><xsl:copy-of select="@querystring"/></xsl:otherwise>
            </xsl:choose>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
   
    <xsl:template name="processQS">
        <xsl:param name="qs"/>
        <xsl:if test="substring-before($qs, '#')">
            <xsl:value-of select="substring-before($qs, '#')"/>
        </xsl:if>
        <xsl:call-template name="processQueryPair">
            <xsl:with-param name="qp" select="substring-before(substring-after($qs, '#') , '#')"/>
        </xsl:call-template>
        <xsl:choose>
            <xsl:when test="contains(substring-after(substring-after($qs, '#') , '#'), '#')">
                <xsl:call-template name="processQS">
                    <xsl:with-param name="qs" select="substring-after(substring-after($qs, '#') , '#')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise><xsl:value-of select="substring-after(substring-after($qs, '#') , '#')"/></xsl:otherwise>
        </xsl:choose>
       
     </xsl:template>

    <xsl:template name="processQueryPair">
        <xsl:param name="qp"/>
        <xsl:param name="arg1" select="substring-before($qp, ':')"></xsl:param>
        <xsl:param name="arg2" select="substring-after($qp, ':')"></xsl:param>
        <xsl:value-of select="document('region.xml')//region[@id=$arg1]/parameter[@id=$arg2]/@value"/>
    </xsl:template>
   
</xsl:stylesheet>
0
 
Geert BormansCommented:
named templates are a bit like functions
and as explained in your previous question, you can pass parameters

processQS progressively eats up the querystring attribute
and copies string parts outside #...# sequences to the output
and calls the template processQueryPair with the sequence inside a #..# as a parameter

note that processQS is setup as a recursive function
that is the only way to make the variables change in XSLT

have a close look at the code and let me know if you don't get it entirely

cheers
0
 
praneethaAuthor Commented:
when exactly is the replace happening.

ok processQueryPair takes what is between # and # and splits with : and gets the value attribute from region.xml.

so its gets the value but when is the replace happening...

1. <xsl:template name="processQS">
  2.     <xsl:param name="qs"/>
    3.    <xsl:if test="substring-before($qs, '#')">
      4.      <xsl:value-of select="substring-before($qs, '#')"/>

i guess we r doing this to get everything before first #

       5. </xsl:if>
       6. <xsl:call-template name="processQueryPair">
         7.   <xsl:with-param name="qp" select="substring-before(substring-after($qs, '#') , '#')"/>
        8.</xsl:call-template>
and here we get the date from region.xml but dont know where we r replacing it

        9.<xsl:choose>
and we r checking again if it has more #

          10.  <xsl:when test="contains(substring-after(substring-after($qs, '#') , '#'), '#')">
             11.   <xsl:call-template name="processQS">
                12.    <xsl:with-param name="qs" select="substring-after(substring-after($qs, '#') , '#')"/>
                13. </xsl:call-template>
            14. </xsl:when>
            15. <xsl:otherwise><xsl:value-of select="substring-after(substring-after($qs, '#') , '#')"/></xsl:otherwise>
        16. </xsl:choose>
       
     17. </xsl:template>

may be u can briefly explain each line..it would help me great deal..:( sorry for the trouble

thanks
0
 
Geert BormansCommented:
no problem

I first continue the numbering
18.   <xsl:template name="processQueryPair">
19.        <xsl:param name="qp"/>
20.        <xsl:param name="arg1" select="substring-before($qp, ':')"></xsl:param>
21.        <xsl:param name="arg2" select="substring-after($qp, ':')"></xsl:param>
22.        <xsl:value-of select="document('region.xml')//region[@id=$arg1]/parameter[@id=$arg2]/@value"/>
23.    </xsl:template>


1. starts the named template "processQS", think of it like a function
2. binds the local parameter "qs" to the parameter passed in using xsl:with-param
3. tests to see there is a part before the first #
4. if there is a part before the first #, it is outputted
between lines: your observation is correct
5. closes the if statement. I do the test here, for the condition that # is the first character, then line 4 would be an issue
6. I call the template processQueryPair (to process the part between the first and second #)
7. construction of the parameter, part after the first # and before the second
8. close calling named template
between lines: the replacing happens in the template processQueryPair, where I have the arguments... line 22.
9. start of a choose, in order to proces the remainder, if there is another #
between lines: your observation is correct
10. test: is there a # left in the remainder, if there is...
11. ... recursively call the processQS template, but with a smaller parameter
...
15 if there is no # in the remainder, just output it

18.   start of template (function) processQueryPair, here the actual querystring is replaced
19.        parameter binding
20.        variable arg1 is the part before the ":"
21.        variable arg2is the part after the ":"
22.        lookup in the other file

hope this helps
cheers
0
 
praneethaAuthor Commented:
thank you so much

i definitely learnt some XSLT thanks to you. :)
0
 
Geert BormansCommented:
welcome
0
 
praneethaAuthor Commented:
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 7
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now