0

I need to modify and existing XML document to concatenate to field values. I'm not sure how to do this, as I'm unable to keep the variables in scope.

Basically if there is a value present for FirstName and LastName, it should output FirstName+LastName. If either or both values for FirstName or LastName are empty, it should not output the FullName. Note this is an existing document and I am unable to modify any existing code above line #34.

Here is what I have so far:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
    xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  <xsl:template match="/">
    <AUTHENTICATOR>
        <USERINFO>
            <xsl:for-each select="/samlp:Response/*[local-name() = 'Assertion']/*[local-name() = 'AttributeStatement']/*">

                <xsl:if test="@Name='First_Name'">
                    <xsl:variable name="firstname" select="*[local-name() = 'AttributeValue']" />
                    <xsl:element name="field">  
                        <xsl:attribute name="name">FirstName</xsl:attribute>    
                        <xsl:attribute name="value">    
                            <xsl:for-each select="*[local-name() = 'AttributeValue']">  
                                <xsl:value-of select="normalize-space(current())"/> 
                            </xsl:for-each> 
                        </xsl:attribute> 
                    </xsl:element> 
                </xsl:if>

                <xsl:if test="@Name='Last_Name'">
                    <xsl:variable name="lastname" select="*[local-name() = 'AttributeValue']" />
                    <xsl:element name="field">  
                        <xsl:attribute name="name">LastName</xsl:attribute>     
                        <xsl:attribute name="value">    
                            <xsl:for-each select="*[local-name() = 'AttributeValue']">  
                                <xsl:value-of select="normalize-space(current())"/> 
                            </xsl:for-each> 
                        </xsl:attribute> 
                    </xsl:element> 
                </xsl:if>

                <!-- Unable to modify anything above this line-->
                <!-- if firstname & lastname not null, concatinate firstname + lastname-->
                <xsl:if test="not($firstname = '') or not($lastname = '')">
                    <xsl:element name="field">
                        <xsl:attribute name="name">FullName</xsl:attribute>
                        <xsl:attribute name="value">
                            <xsl:value-of select="concat($firstname,'+',$lastname)" />
                        </xsl:attribute>
                    </xsl:element>
                </xsl:if>

            </xsl:for-each>

        </USERINFO>
    </AUTHENTICATOR>
  </xsl:template>
</xsl:stylesheet>

Simplified XML file below:

<samlp:Response ID="_f9daea33-e32a-4dde-beb4-d5227690b1a3" Version="2.0" IssueInstant="2015-07-30T15:06:58.874Z" Destination="https://domain.net/Login/PAuthentication.aspx?configSet=SAML" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:jh:identityprovider</saml:Issuer>
<samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</samlp:Status>
<saml:Assertion Version="2.0" ID="_b54ca592-4401-4107-a426-281918091842" IssueInstant="2015-07-30T15:06:58.898Z" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
    <saml:AttributeStatement>
        <saml:Attribute Name="FirstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
            <saml:AttributeValue>John</saml:AttributeValue>
        </saml:Attribute>
    </saml:AttributeStatement>
            <saml:AttributeStatement>
        <saml:Attribute Name="LastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
            <saml:AttributeValue>Doe</saml:AttributeValue>
        </saml:Attribute>
    </saml:AttributeStatement>
</saml:Assertion>

PixelPaul
  • 2,609
  • 4
  • 39
  • 70
  • Could you show an example (preferably minimized) of the input XML? – michael.hor257k Sep 25 '15 at 17:18
  • refer [How to check string is empty or null in XSLT][1] [1]: http://stackoverflow.com/questions/825831/check-if-a-string-is-null-or-empty-in-xslt – rupesh_padhye Sep 25 '15 at 17:51
  • From your [previous question](http://stackoverflow.com/questions/32764372/xslt-assign-variable-value-from-xml), you learned how to **not** use `local-name()`. You should really update your XSLT with that answer, as using `local-name` is confusing and error-prone (as Michael shows in the answer here as well). – Abel Sep 25 '15 at 20:45

1 Answers1

1

The example is rather confusing - it's not clear what is a "record" and how many records can you have (judging by your XSLT, only one?).

See if you can use this as your starting point. It assumes each saml:Assertion is a record, and it outputs a FullName field if the record has both a FirstName and a LastName.

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
exclude-result-prefixes="samlp saml">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/samlp:Response">
    <AUTHENTICATOR>
        <xsl:for-each select="saml:Assertion">
            <USERINFO>
                <xsl:variable name="firstname" select="saml:AttributeStatement/saml:Attribute[@Name='FirstName']/saml:AttributeValue"/>
                <xsl:variable name="lastname" select="saml:AttributeStatement/saml:Attribute[@Name='LastName']/saml:AttributeValue"/>
                <xsl:if test="$firstname and $lastname">
                    <field name="FullName" value="{concat($firstname, ' ', $lastname)}"/>  
                </xsl:if>
            </USERINFO>         
        </xsl:for-each>
    </AUTHENTICATOR>        
</xsl:template>

</xsl:stylesheet>

Test input

<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
   <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
      <saml:AttributeStatement>
         <saml:Attribute Name="FirstName">
            <saml:AttributeValue>John</saml:AttributeValue>
         </saml:Attribute>
      </saml:AttributeStatement>
      <saml:AttributeStatement>
         <saml:Attribute Name="LastName">
            <saml:AttributeValue>Doe</saml:AttributeValue>
         </saml:Attribute>
      </saml:AttributeStatement>
   </saml:Assertion>
   <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
      <saml:AttributeStatement>
         <saml:Attribute Name="FirstName">
            <saml:AttributeValue>Madonna</saml:AttributeValue>
         </saml:Attribute>
      </saml:AttributeStatement>
   </saml:Assertion>
</samlp:Response>

Result

<?xml version="1.0" encoding="UTF-8"?>
<AUTHENTICATOR>
  <USERINFO>
    <field name="FullName" value="John Doe"/>
  </USERINFO>
  <USERINFO/>
</AUTHENTICATOR>

Note the use of prefixes to call out the elements in the source XML.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • Good to see that your approach is also to remove those confusing `local-name()` calls. I suggested in [a previous answer to this same user, same data](http://stackoverflow.com/questions/32764372/xslt-assign-variable-value-from-xml) to use something clearer than `saml` and `samlp` as mixing them up was then the cause of getting wrong results. – Abel Sep 25 '15 at 20:47