1

I need help from experts here to optimize the solution of updating string value in an element . I have this xml file as an input...

<?xml version="1.0" encoding="UTF-8"?>
<FullRequest>
    <Header>
        <Looptimes>3</Looptimes>
        <SomeElement>blah!</SomeElement>
        <AnotherElement>blah!!</AnotherElement>
    </Header>
    <RequestDetail>
        <!-- Request Element is a string of fixed length (50 characters) -->
        <Request>THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG AGAIN!</Request>
        <Request>THE TIME FOX JUMPED OVER THE LAZY DOG, PROGRESSED!</Request>
        <Request>OWING TO THE WIDESPREAD KNOWLEDGE OF THE PHRASE AN</Request>
    </RequestDetail>
</FullRequest>

Offset 5 in Request element will be unique and can be cross-referenced. Q, T and G are the IDs in the above request.

<?xml version="1.0" encoding="UTF-8"?>
<FullResponse>
    <Header>
        <Looptimes>3</Looptimes>
        <SomeElement>blah!</SomeElement>
        <AnotherElement>blah!!</AnotherElement>
    </Header>
    <ResponseDetail>
        <!-- Response element repeats for 3 times as indicated by the value of Looptimes -->
        <!-- Id has a unique value -->
        <Response>
            <Id>Q</Id>
            <Value1>ABC</Value1>
            <Value2>XYZ</Value2>
            <Value3>FGK</Value3>
        </Response>
        <Response>
            <Id>T</Id>
            <Value1>123</Value1>
            <Value2>YOK</Value2>
            <Value3>DSL</Value3>
        </Response>
        <Response>
            <Id>G</Id>
            <Value1>BAT</Value1>
            <Value2>TKR</Value2>
            <Value3>LAF</Value3>
        </Response>
    </ResponseDetail>
</FullResponse>

Taking the above xml, offset positions 10, 15 and 20 need to be replaced with values Value1, Value2 and Value3 respectively.

I have this XSL which does the job. Not sure how good this solution will work with few thousand records of 5000 characters each (50 characters in the Request element shown as an example here) with about 20 locations to edit.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:regexp="http://exslt.org/regular-expressions" exclude-result-prefixes="regexp">
    <xsl:output omit-xml-declaration="no" indent="yes"/>
    <xsl:preserve-space elements="*"/>

    <xsl:variable name="WebResponse" select="document('local:///ic1-data.xml')"/>
    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>

    </xsl:template>

    <xsl:template match="/FullRequest/RequestDetail/Request">
        <xsl:variable name="currentLine" select="."/>
        <xsl:variable name="id" select="substring($currentLine,5,1)"/>
        <xsl:for-each select="$WebResponse/FullResponse/ResponseDetail/Response">
            <xsl:variable name="ResId" select="Id"/>

            <xsl:if test="$id = $ResId">
                <xsl:element name="{name()}">
                    <!-- Update Value1 -->
                    <xsl:variable name="from" select="substring($currentLine,10,3)"/>
                    <xsl:variable name="UpdatedValue1"
                        select="regexp:replace($currentLine,$from,'',Value1)"/>

                    <!-- Update Value2 -->
                    <xsl:variable name="from2" select="substring($UpdatedValue1,15,3)"/>
                    <xsl:variable name="UpdatedValue2"
                        select="regexp:replace($UpdatedValue1,$from2,'',Value2)"/>

                    <!-- Update Value3 -->
                    <xsl:variable name="from3" select="substring($UpdatedValue2,20,3)"/>
                    <xsl:value-of select="regexp:replace($UpdatedValue2,$from3,'',Value3)"/>
                </xsl:element>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

The sample output will be as:

<?xml version="1.0" encoding="UTF-8"?>
<FullRequest>
    <Header>
        <Looptimes>3</Looptimes>
        <SomeElement>blah!</SomeElement>
        <AnotherElement>blah!!</AnotherElement>
    </Header>
    <RequestDetail>
        <Response>THE QUICKABCOWXYZOXFGKMPS OVER THE LAZY DOG AGAIN!</Response>
        <Response>THE TIME 123 JYOKEDDSLER THE LAZY DOG, PROGRESSED!</Response>
        <Response>OWING TO BAT WTKRSPLAFD KNOWLEDGE OF THE PHRASE AN</Response>
    </RequestDetail>
</FullRequest>

I can only use XSLT 1.0

Can you suggest how to make this better?

Thanks.

Wayne
  • 59,728
  • 15
  • 131
  • 126
Liv2luv
  • 15
  • 2

1 Answers1

1

Another more flexible approach would be:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:key name="kResponseById" match="Response" use="Id"/>
    <xsl:variable name="WebResponse" select="document('ic1-data.xml')"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Request/text()">
        <xsl:variable name="vCurrent" select="."/>
        <xsl:for-each select="$WebResponse">
            <xsl:call-template name="replace">
                <xsl:with-param name="pString" select="$vCurrent"/>
                <xsl:with-param name="pValues"
                     select="key('kResponseById',
                                 substring($vCurrent,5,1)
                             )/*[starts-with(local-name(),'Value')]"/>
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>
    <xsl:template name="replace">
        <xsl:param name="pString"/>
        <xsl:param name="pValues"/>
        <xsl:choose>
            <xsl:when test="$pValues">
                <xsl:variable name="pLimit"
                    select="substring-after(
                               local-name($pValues[1]),
                               'Value'
                            ) * 5 + 4"/>
                <xsl:call-template name="replace">
                    <xsl:with-param name="pString"
                         select="concat(
                                    substring($pString, 1, $pLimit),
                                    $pValues[1],
                                    substring($pString, $pLimit + 4)
                                 )"/>
                    <xsl:with-param name="pValues"
                         select="$pValues[position()!=1]"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$pString"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Output:

<FullRequest>
    <Header>
        <Looptimes>3</Looptimes>
        <SomeElement>blah!</SomeElement>
        <AnotherElement>blah!!</AnotherElement>
    </Header>
    <RequestDetail>
        <!-- Request Element is a string of fixed length (50 characters) -->
        <Request>THE QUICKABCOWXYZOXFGKMPS OVER THE LAZY DOG AGAIN!</Request>
        <Request>THE TIME 123 JYOKEDDSLER THE LAZY DOG, PROGRESSED!</Request>
        <Request>OWING TO BAT WTKRSPLAFD KNOWLEDGE OF THE PHRASE AN</Request>
    </RequestDetail>
</FullRequest>
  • Great! intelligent solution. You have very well used the offset locations which are a bit symmetrical in the example scenario. I guess the actual thing would be random. Which I will attempt to make it on the suggested approach. Thank you once again. – Liv2luv Mar 30 '11 at 19:00
  • @Liv2luv: You are welcome. If the offset doesn't follow a formula, you can use an inline or external map. Or, if the offset function is more complex than mapping, you could use pattern matching with modes when applying templates to `ValueN` elements. –  Mar 30 '11 at 19:36