yekootmada
yekootmada

Reputation: 31

XSLT to return string between two characters

I am trying to return part of a string in an XML sheet. I want to keep what is between the brackets in the "Description" field, and move what is outside the brackets to a different field:

The Source XML:

<Log>
   <Date>15-Nov-2014</Date>
   <TC>14:38:47:13</TC>
   <Description>Move this text (but keep this text here)</Description>
</Log>

Desired result is:

<Log>
   <Date>15-Nov-2014</Date>
   <TC>14:38:47:13</TC>
   <Description>but keep this text here</Description>
   <MyOtherField>Move this text</MyOtherField> 
</Log>

I have tried using the substring-before "(" and 'substring-after ")" functions in line and the output is almost correct - however the snag is that not all the "Description" fields in my source XML sheet have text in brackets, so those are being deleted completely.

Any assistance would be greatly appreciated.

Thank you

Upvotes: 0

Views: 9776

Answers (3)

Daniel Haley
Daniel Haley

Reputation: 52888

Since you're using XSLT 2.0, you could use xsl:analyze-string to get the text between the parens...

XML Input

<Log>
    <Date>15-Nov-2014</Date>
    <TC>14:38:47:13</TC>
    <Description>Move this text (but keep this text here)</Description>
</Log>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="Description">
      <xsl:copy>
        <xsl:analyze-string select="." regex="\(([^)]+)\)">
          <xsl:matching-substring>
            <xsl:value-of select="regex-group(1)"/>
          </xsl:matching-substring>
        </xsl:analyze-string>
      </xsl:copy>
      <MyOtherField>
        <xsl:value-of select="normalize-space(string-join((substring-before(concat(.,'('),'('),
          substring-after(.,')')),' '))"/>
      </MyOtherField>      
  </xsl:template>

</xsl:stylesheet>

XML Output

<Log>
   <Date>15-Nov-2014</Date>
   <TC>14:38:47:13</TC>
   <Description>but keep this text here</Description>
   <MyOtherField>Move this text</MyOtherField>
</Log>

Here it is integrated into your existing code: http://xsltransform.net/pPzifq1/1

Upvotes: 1

Dan Field
Dan Field

Reputation: 21661

I was able to achieve this using the following template:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />

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

    <xsl:template match="Description[contains(text(), '(')]">
        <Description>
            <xsl:value-of select="substring-before(substring-after(., ' ('), ')')" />
        </Description>
        <MyOtherField>
            <xsl:value-of select="substring-before(., ' (')" />
        </MyOtherField>
    </xsl:template>
</xsl:transform>

Note that it may not work as expected if you have multiple (s or )s. The idea is that you match any Description tags that contain a ( in them, and then use the substring logic.

http://xsltransform.net/gWvjQfU

Upvotes: 0

michael.hor257k
michael.hor257k

Reputation: 117165

Try it this way:

<xsl:template match="Description">
    <xsl:copy>
        <xsl:value-of select="substring-before(substring-after(., '('), ')')"/>
    </xsl:copy>
    <MyOtherField>
        <xsl:value-of select="substring-before(concat(., '('), '(')"/>
    </MyOtherField>
</xsl:template>

Upvotes: 4

Related Questions