8 Replies Latest reply on Feb 2, 2016 7:55 AM by Jeremy Winslow

    XSLT Transform - Case insensative

      Hi all,

       

      Is there any solution to a "case-insensative" XSLT Transform?

       

      We have an XSLT transform that is trying to "append" an XML doclet to a larger XML doclet.  The smaller XML doclet can be classified as either an <Input> or <Output> parameter.  In the Transform, we use For loops to iterate thru the <Input> and <Output> sections of the larger XML doclet.  A simple Switch statement determines which XSLT Transform we use, append an <Input> doclet or append an <Output> doclet.

       

      The problem is the larger XML doclet may have <Input>, <input>, <Output>, <output> as the section elements...There is also a parent XML element of <Input> and <Output> called <Parameters> that can be either <Parameters> or <parameters>.  So the need to be case-insensative is important.

       

      Right now, I have For loops in the XSLT Transform that copy based on an XPath of /parameters/input/parameter when appending to the Input section.  When appending to the Output section we use an XPath of /parameters/output/parameter.  But this doesn't work when the larger XML doclet has elements with capitals as the first letter...i.e. Parameters/Output/Parameter...

       

      Input XSLTOutput XSLT

      XSLT Stylesheet

          Template: Match=/

              Element: parameters

                  Element: input

                      For each: Select-"/parameters/input/parameter"

                          Copy of: Select-"."

                      Copy

                          Token(s): ${parameter}

                  For each: Select-"/parameters/output"

                      Copy of: Select-"."

      XSLT Stylesheet

          Template: Match=/

              Element: parameters

                  For each: Select-"/parameters/input"

                      Copy of: Select-"."

                  Element: output

                      For each: Select-"/parameters/output/parameter"

                          Copy of: Select-"."

                      Copy

                          Token(s): ${parameter}

      - The first For loop copies individual input/parameter elements.

      - The Copy then appends the XML parameter as needed.

      - The second For loop copies the entire <output> element as a whole.

      - The first For loop copies the entire <input> element.

      - The second For loop copies individual <output> elements.

      - The Copy then appends the XML parameter as needed.

       

      Things I've tried:

      - Translate() - I tried using a Translate() around the XPath values in the For loop, but the XSLT just exceptions out.

      - Case sensative For Loops - I added additional For loops, each one with a different case-sensative XPath, but this got ugly...

       

                  Element: input

                      For each: Select-"/parameters/input/parameter"

                          Copy of: Select-"."

                      For each: Select-"/Parameters/input/parameter"

                          Copy of: Select-"."

                      For each: Select-"/Parameters/Input/parameter"

                          Copy of: Select-"."

                      For each: Select-"/Parameters/Input/Parameter"

                          Copy of: Select-"."

                      Copy

                          Token(s): ${parameter}

       

       

      So, as an example, this is what I want to do:

       

      IO-Type : input

       

      xml-parameter :

       

      <Parameter>

          <Name required="true">user_name</Name>

          <Value type="xs:string">

              <Text>[hpsc-usr]</Text>

          </Value>

      </Parameter>

       

       

       

      xml-parameters :

       

      <parameters>

          <Input>

              <Parameter>

       

                  <Name required="true">requestor</Name>

                  <Value type="xs:string">

                      <Text>imop</Text>

                  </Value>

              </Parameter>

       

          </Input>

       

          <Output>

              <Parameter>

                  <Name required="false">event_log</Name>

              </Parameter>

       

          </Output>

      </parameters>

       

       

      Desired Result:

       

      <parameters>

          <Input>

              <Parameter>

       

                  <Name required="true">requestor</Name>

                  <Value type="xs:string">

                      <Text>imop</Text>

                  </Value>

              </Parameter>

       

              <Parameter>

                  <Name required="true">user_name</Name>

                  <Value type="xs:string">

                      <Text>[hpsc-usr]</Text>

                  </Value>

              </Parameter>

       

          </Input>

       

          <Output>

       

              <Parameter>

                  <Name required="false">event_log</Name>

              </Parameter>

       

          </Output>

      </parameters>

       

       

       

      Thx in advance,

       

      adym

        • 1. XSLT Transform - Case insensative
          Richard De Vries

          Adym, have you ever had a look at my XML Utilities workflows? I am wondering if doing an "XML to XML String", followed by a lower-case(.) on that XML String and finally "XML String to XML" would get you what you need?

          • 2. XSLT Transform - Case insensative

            Richard,

             

            Actually, I have a solution that does this using your XML utilities.  The problem is some of the <parameters> are actual SOAP XML Requests that need to preserve the Capitals in the text.  Unfortunately, the XML Utilities do the "entire" XML structure, including the <parameter> content.

             

            So the solution had to be abandoned...

             

            hth,

             

            adym

            • 3. Re: XSLT Transform - Case insensative
              Richard De Vries

              Adym; I have updated my XML Utilities with two additional workflows:

               

              - Convert Element Names to Lower Case

              - Convert Element Names to Upper Case

               

              I am not currently connected to the GRID (doing this offline) so I haven't been able to fully test it yet (I just tested the transforms in my transform editor), but have a look at these. Hopefully they'll do what you want. Basically, any XML structure that is being ran through one of these workflows will have all of the element names converted to either lower-case or upper-case. The values will NOT be affected.

               

              Once I get these *officially* tested on Monday, I will update my posted document as well, but for now, here you go.

               

              Keep me posted.

               

              Cheers,

               

                 Richard

              • 4. Re: XSLT Transform - Case insensative
                Andrew Lord

                cant you use

                 

                /*[name()='Parameters' or name()='parameters']/*[name()='Input' or name()='input']/*[name()='Parameter' or name()='parameter']

                 

                not pretty but ...

                 

                [ oops yes local-name() might be better if namespaces etc ]

                • 5. Re: XSLT Transform - Case insensative

                  Firstly set the token value that represents the new Parameter/parameter without its parent element using a String Parametter type in the Assign Activity

                   

                  <Name required="true">In Param 1</Name>

                  <Value type="xs:string">

                       <Text>One</Text>

                  </Value>

                   

                  Refer to this new context item as Token name "new param" in the following XSLT which you can load into the Dev Studio XSLT editor (post back if you do not know abouth the XSLT Samples folder in the resources area).

                   

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

                    <xsl:output method="xml" />

                    <xsl:template match="/">

                      <xsl:variable name="root" select="*" />

                      <xsl:text disable-output-escaping="yes">&lt;</xsl:text>

                      <xsl:value-of select="local-name($root)" />

                      <xsl:text disable-output-escaping="yes">&gt;</xsl:text>

                      <xsl:variable name="input" select="$root/child::*[local-name()='Input' or local-name()='input' ]" />

                      <xsl:text disable-output-escaping="yes">&lt;</xsl:text>

                      <xsl:value-of select="local-name($input)" />

                      <xsl:text disable-output-escaping="yes">&gt;</xsl:text>

                      <xsl:copy-of select="$input/child::*[local-name()='parameter' or local-name()='Parameter' ]" />

                      <xsl:variable name="parameter" select="$input/child::*[local-name()='parameter' or local-name()='Parameter' ][1]" />

                      <xsl:text disable-output-escaping="yes">&lt;</xsl:text>

                      <xsl:value-of select="local-name($parameter)" />

                      <xsl:text disable-output-escaping="yes">&gt;</xsl:text>

                      ${new param}

                      <xsl:text disable-output-escaping="yes">&lt;/</xsl:text>

                      <xsl:value-of select="local-name($parameter)" />

                      <xsl:text disable-output-escaping="yes">&gt;</xsl:text>

                      <xsl:text disable-output-escaping="yes">&lt;/</xsl:text>

                      <xsl:value-of select="local-name($input)" />

                      <xsl:text disable-output-escaping="yes">&gt;</xsl:text>

                      <xsl:copy-of select="$root/child::*[local-name()='Output' or local-name()='output' ]" />

                      <xsl:text disable-output-escaping="yes">&lt;/</xsl:text>

                      <xsl:value-of select="local-name($root)" />

                      <xsl:text disable-output-escaping="yes">&gt;</xsl:text>

                    </xsl:template>

                  </xsl:stylesheet>

                   

                  The above is generic, and utilizes all the element names found in the input document to generate the output document (I hope this is the funcitonality you wanted).

                   

                  Module attached with workflow showing the above.  Just copy/edit the XSLT and switch the input and output logic to achieve your second use case.

                   

                  Regards,

                   

                  Robin.

                  • 6. Re: XSLT Transform - Case insensative
                    Andrew Lord

                    Alternatively use the identity transform and add another template to just insert your XML at the correct point.

                    For all the other parameters the identity transform will copy verbatim.

                     

                    for input mode the additional template will have the identity transform and also the Copy-Of for the Xml fragment.

                     

                    [see post below]

                    • 7. Re: XSLT Transform - Case insensative
                      Andrew Lord

                      Heres an example for your input XSLT using the identity transform. Replace the <new_stuff> bit with your token.

                       

                       

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

                        <xsl:template match="@*|node()">

                          <xsl:copy>

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

                          </xsl:copy>

                        </xsl:template>

                       

                        <xsl:template match="Parameters/input | Parameters/Input | parameters/input | parameters/Input">

                          <xsl:copy>

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

                             <new_stuff/>

                          </xsl:copy>

                        </xsl:template>

                      </xsl:stylesheet>

                       

                      I found the following page very helpful when trying to understand using the identity transform and how template matching works.

                      http://lenzconsulting.com/how-xslt-works/

                      • 8. Re: XSLT Transform - Case insensative
                        Jeremy Winslow

                        Hi Adym,

                        Depending on your version of XSLT, v 1.0 does not provide simple functions such as case-upper() or case-lower() this functionality is only provided in v 2.0. I had a similar issue and used a transform function to match case-insensitive. Below I was using this to match on a wmiService down from running a wmic command on the target box. I highlighted in bold the transform using an input adapter response to to match on a token string '${matchStr}'. As long as all the characters are present, regardless of any spaces or cases.

                         

                        The contains() function combined with translate() replaces all lower case characters in both strings and replaces the lowercase matched character with an uppercase character. The transform provides the matching criteria where contains() provides the match function.

                         

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

                          <xsl:output indent="no" />

                          <xsl:template match="/">

                            <output>

                              <xsl:if test=". != ''">

                                <xsl:if test="//line[. != '']">

                                  <xsl:for-each select="//line">

                                    <xsl:if test="not(contains(., &quot;No Instance(s) Available.&quot;))">

                                      <xsl:if test="not(contains(., &quot;invalid&quot;))  or not(contains(., &quot;error&quot;))">

                                        <xsl:choose>

                                          <xsl:when test="contains(translate(.,'abcdefghijklmnopqrstuvwxyz ','ABCDEFGHIJKLMNOPQRSTUVWXYZ') , translate('${matchStr}','abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'))">

                                            <result>

                                              <xsl:value-of select="normalize-space(.)" disable-output-escaping="no" />

                                            </result>

                                          </xsl:when>

                                        </xsl:choose>

                                      </xsl:if>

                                      <xsl:if test="contains(., &quot;invalid&quot;)">

                                        <result>

                                          <xsl:text disable-output-escaping="no">No Match</xsl:text>

                                        </result>

                                      </xsl:if>

                                    </xsl:if>

                                    <xsl:if test="contains(., &quot;No Instance(s) Available.&quot;)">

                                      <return>

                                        <xsl:text disable-output-escaping="no">No Match</xsl:text>

                                      </return>

                                    </xsl:if>

                                  </xsl:for-each>

                                </xsl:if>

                                <xsl:if test="//line[. =  '']">

                                  <result>

                                    <xsl:text disable-output-escaping="no">No Match</xsl:text>

                                  </result>

                                </xsl:if>

                              </xsl:if>

                              <xsl:if test=". = ''">

                                <result>

                                  <xsl:text disable-output-escaping="no">No Match</xsl:text>

                                </result>

                              </xsl:if>

                            </output>

                          </xsl:template>

                        </xsl:stylesheet>