1

The text input should be converted to the XML output using XSLT 1.0. The list is of variable length with the delimiter being |.

Input:

name=IMON_EVENT;next_state=SET_IMON;is_enabled=true; | name=MAIN_BATCH;next_state=BATCH01;is_enabled=false;priority=9;

Expected output:

<time-triggers>
    <trigger>
        <name>IMON_EVENT</name>
        <next_state>SET_IMON</next_state>
        <is_enabled>true</is_enabled>
    </trigger>
    <trigger>
        <name>MAIN_BATCH</name>
        <next_state>BATCH01</next_state>
        <is_enabled>false</is_enabled>
        <priority>9</priority>
    </trigger>
</time-triggers>
Shikha
  • 13
  • 5
  • 2
    So which XSLT version do you use/can you use? XSLT 2 has `tokenize` and `xsl:analyze-string`, XSLT 3 additionally has the `analyze-string` function. – Martin Honnen Sep 08 '18 at 12:18
  • First [split at `|`](https://stackoverflow.com/a/4845895/11683), then split each result again at `;`, then split each result again at `=`... – GSerg Sep 08 '18 at 12:18
  • @MartinHonnen I have to use XSLT 1.0 without node-set function. – Shikha Sep 10 '18 at 11:08

2 Answers2

0

For an XSLT 1.0, you can use recursive template calls that test for the presence of the delimiters and use substring-before() and substring-after()

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:output indent="yes"/>

    <xsl:template match="/">
        <xsl:variable name="triggers" select="'name=IMON_EVENT;next_state=SET_IMON;is_enabled=true; | name=MAIN_BATCH;next_state=BATCH01;is_enabled=false;priority=9;'"/>

        <triggers>
            <xsl:call-template name="make-trigger">
                <xsl:with-param name="val" select="$triggers"/>
            </xsl:call-template> 
        </triggers>   
    </xsl:template>

    <xsl:template name="make-trigger">
        <xsl:param name="val"/>

        <xsl:if test="normalize-space($val)">
            <xsl:choose>
                <xsl:when test="contains($val, '|')">
                    <trigger>
                    <xsl:call-template name="make-elements">
                        <xsl:with-param name="val" select="substring-before($val, '|')"/>
                    </xsl:call-template>
                    </trigger>
                    <xsl:call-template name="make-trigger">
                        <xsl:with-param name="val" select="substring-after($val, '|')"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <trigger>
                    <xsl:call-template name="make-elements">
                        <xsl:with-param name="val" select="$val"/>
                    </xsl:call-template>
                    </trigger>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>

    <xsl:template name="make-elements">
        <xsl:param name="val"/>

        <xsl:if test="contains($val, '=')">
            <xsl:choose>
                <xsl:when test="contains($val, ';')">
                    <xsl:call-template name="make-element">
                        <xsl:with-param name="val" select="substring-before($val, ';')"/>
                    </xsl:call-template>
                    <xsl:call-template name="make-elements">
                        <xsl:with-param name="val" select="substring-after($val, ';')"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:call-template name="make-element">
                        <xsl:with-param name="val" select="$val"/>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>

    <xsl:template name="make-element">
        <xsl:param name="val"/>

        <xsl:element name="{normalize-space(substring-before($val, '='))}">
            <xsl:value-of select="substring-after($val, '=')"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

Below is an XSLT 2.0 solution that uses the tokenize() function to split the values by | and ; delimiters with nested xsl:for-each to process the sequence of values, xsl:analyze-string to capture the name and value between the =, and xsl:element to create dynamically named elements from the regex capture groups.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">

  <xsl:output indent="yes"/>

  <xsl:template match="/">
    <xsl:variable name="triggers" select="'name=IMON_EVENT;next_state=SET_IMON;is_enabled=true; | name=MAIN_BATCH;next_state=BATCH01;is_enabled=false;priority=9;'"/>

    <triggers>
      <xsl:for-each  select="tokenize($triggers, '\s*\|\s*')">
        <trigger>
          <xsl:for-each select="tokenize(., '\s*;\s*')">
              <xsl:analyze-string select="." regex="(.+)=(.*)">
                  <xsl:matching-substring>
                      <xsl:element name="{regex-group(1)}">
                          <xsl:value-of select="regex-group(2)"/>
                      </xsl:element>
                  </xsl:matching-substring>
              </xsl:analyze-string> 
          </xsl:for-each>
        </trigger>  
      </xsl:for-each>
    </triggers>

  </xsl:template>

</xsl:stylesheet>
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • How can I do it in XSLT 1.0 without using the node-set function? – Shikha Sep 10 '18 at 11:07
  • I have added an XSLT 1.0 solution that uses recursive template calls. If you can use an XSLT 2.0 engine, everything becomes much easier with the power of regex for string splitting and pattern matching capabilities. – Mads Hansen Sep 19 '18 at 02:36
0

Placement of the source string in XSLT script is a bad practice, especially if you want to process various inputs.

If you can use XSLT version 2.0, you should rather use the following functions:

  • unparsed-text-available - to check whether an input file exists,
  • unparsed-text - to read the content of this file into a variable.

The rest of the script (how to process the content read) can be as in the other answer.

Valdi_Bo
  • 30,023
  • 4
  • 23
  • 41