Re: New XSLT NOT WORKING was Re: [xsl] XSLT to populate a SAML AttributeStatement from an XML

Subject: Re: New XSLT NOT WORKING was Re: [xsl] XSLT to populate a SAML AttributeStatement from an XML
From: "Wendell Piez wapiez@xxxxxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Sat, 21 Jan 2023 15:32:57 -0000
Jim,

"Hidden XSLT" is an interesting thing to call it. What is being referred to
by this is the implicit or default behaviors that are wired into an XSLT
processor, that do things on your behalf without your asking for it. This
is both a blessing and  (of course) a curse.

To avoid having to curse, the secret is to learn about the XSLT processing
model and how XSLT templates, instructions, and fragments of results
(actually: templates of fragments of results) all work together.

Key to this is to understand "node traversal" along the tree-shaped data
model, along with details such as that when you say:

<xsl:apply-templates/>

this is functionally equivalent to the longer and more explicit instruction

<xsl:apply-templates select="child::node()"/>

which requires some XPath knowledge to decipher.

The effect of this is to select nodes from the tree (from the *context*)
and process them by applying templates to them. While by default this works
one step at a time (i.e. from parents to children), this traversal can also
be altered, by either of two ways.

1. You can select something besides the children (elements, text,
processing instructions and comments), such as attributes (select="@*") or
descendants (select=".//p")
2. You can passively interrupt the traversal by matching some node and
doing nothing.

So <xsl:template match="p"/> will have the effect of matching any p element
and forestalling any further "reaching" into the node tree for data - with
the result that all 'p' elements *and all their contents* are omitted (this
time). (They might be handled differently in different modes.)

Looking at your problem, however I think you want no 1 (a "pull") not no 2
(a "push"), but with templates deeper in your logic to handle your
conditionals (mappings), i.e. something like

> <adrRecord>
>    <PN_ID>1111111</PN_ID>
>    <personnel>
>      <ADM_ORG_CD>urn:NORM:DEPT</ADM_ORG_CD>
>      <DOD_ASSOC_CD>urn:NORM:V01</DOD_ASSOC_CD>
>    </personnel>
>  </adrRecord>
> </record>

<xsl:template match="/">
  <attributes>
     <xsl:apply-templates select="//PN_ID | //ADM_ORG_CD | //DOD_ASSOC_CD"
mode="write-saml"/>
  </attributes>
</xsl:template>

<!-- this match is a wildcard because we template the same for everything
in this mode -->
<xsl:template match=" * " mode="write-saml">
  <xsl:variable name="new-name">
    <!-- but we call another template to determine the name of our thing -->
    <xsl:apply-templates select="." mode="saml-attribute-name"/>
  </xsl:variable>
  <saml:Attribute Name="{ $new-name }"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <xsl:call-template name="split-saml"/>
   </saml:Attribute>
</xsl:template>

<!--- add one of these for each of your differentiated inputs -->
<xsl:template match=" ADM_ORG_CD " mode="saml-attribute-name">
MY_ORG_CD</xsl:template>

<!-- we split this out just to be more concise above -->
<xsl:template name="split-saml">
  <xsl:for-each select="tokenize(.,':')">
     <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
xsi:type="xsd:string">
      <xsl:value-of select="."/>
     </saml:AttributeValue>
    </xsl:for-each>
</xsl:template>

Hope that helps - note, untested and free-typed by actual human fingers.

Cheers, Wendell


On Sat, Jan 21, 2023 at 1:57 AM ohaya ohaya@xxxxxxxxx <
xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:

> I tried what Michael suggested, but it is not working correctly.
>
> Here's the XML file:
>
> <?xml version="1.0"?>
> <record>
> <adrRecord>
> <PN_ID>1111111</PN_ID>
> <personnel>
> <ADM_ORG_CD>urn:NORM:DEPT</ADM_ORG_CD>
> <DOD_ASSOC_CD>urn:NORM:V01</DOD_ASSOC_CD>
> </personnel>
> <enterpriseUser>
> <entUserRoles>a:b:c</entUserRoles>
> </enterpriseUser>
> </adrRecord>
> </record>
>
> Here's the XSLT I have now:
>
> <xsl:stylesheet version="1.0"
> xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
>
>
> <xsl:template match="/">
> <saml:AttributeStatement>
> <!-- <xsl:apply-templates select="/record/adrRecord/*"/> -->
> <xsl:apply-templates/>
> </saml:AttributeStatement>
> </xsl:template>
>
> <!--<xsl:template match="text()|@*" mode="#all"/> -->
> <xsl:template match="text()"/>
>
> <xsl:template match="/record/adrRecord/*">
> <saml:Attribute Name="{name()}"
> NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
> <xsl:for-each select="tokenize(.,':')">
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string"><xsl:value-of select="."/></saml:AttributeValue>
> </xsl:for-each>
> </saml:Attribute>
> </xsl:template>
>
> </xsl:stylesheet>
>
>
> The output:
>
> <?xml version="1.0"?>
> <saml:AttributeStatement
> xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="
> http://www.w3.org/2001/XMLSchema-instance";>
> <saml:Attribute Name="PN_ID"
> NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">1111111</saml:AttributeValue>
> </saml:Attribute>
> <saml:Attribute Name="personnel"
> NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">
> urn</saml:AttributeValue>
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">NORM</saml:AttributeValue>
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">DEPT
> urn</saml:AttributeValue>
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">NORM</saml:AttributeValue>
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">V01
> </saml:AttributeValue>
> </saml:Attribute>
> <saml:Attribute Name="enterpriseUser"
> NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">
> a</saml:AttributeValue>
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">b</saml:AttributeValue>
> <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">c
> </saml:AttributeValue>
> </saml:Attribute>
> </saml:AttributeStatement>
>
> It seems like it is picking up the path names instead of the element/node
> names?
>
> Also, FYI, I have another test with more/actual data, and that is also
> showing an additional problem... similar to previously, it is outputting a
> bunch of "extraneous data"... it is outputting a bunch of the attribute
> values, without any XML. My understanding from the earlier discussion about
> this problem is that this extraneous data is happening because of the
> "hidden XSLT", but I've tried several different thing to try to suppress
> that but neither have suppressed that data output:
>
> <xsl:template match="text()|@*" mode="#all"/>
> and:
>
> <xsl:template match="text()"/>
>
> I know that I am either misunderstanding something that Michael had said,
> or missing something :(...
>
> Thanks,
> Jim
> On Saturday, January 21, 2023, 12:14:33 AM EST, ohaya ohaya@xxxxxxxxx <
> xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
>
>
> Hi Michael (et al),
>
> You all might find this funny (== "ironic" or "amusing" ), but after
> showing some of my colleagues what I had done, they are now telling me (a)
> the names for each of the template matches ARE the same (exactly as you all
> ASSUMED earlier) plus they realized that if the XSLT designed to only work
> with specific names, then if the names in the source XML changed, then we
> would also have to change our code every time, so I think I am going to try
> to to use what you (Michael) originally suggested :)!!
>
> Jim
>
> On Wednesday, January 18, 2023, 01:56:45 PM EST, Michael Kay
> michaelkay90@xxxxxxxxx <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
>
>
> Are you able to use XSLT 2.0+ -- that gives you a tokenize() function
> which makes this far easier.
>
> In 2.0 it's essentially
>
> <xsl:template match="personnel/*">
>   <saml:Attribute Name="{name()}"
> NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
>     <xsl:for-each select="tokenize(.)">
>       <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string"><xsl:value-of select="."/></saml:AttributeValue>
>
> Incidentally, there are no attributes in your input XML - it's all
> elements.
>
> Michael Kay
> Saxonica
>
>
> > On 18 Jan 2023, at 17:06, ohaya ohaya@xxxxxxxxx <
> xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
> >
> > [I originally sent this to the wrong email address... sorry!]
> >
> > Hi,
> >
> > I have an application where I am retrieving a number of attributes and
> values from a web service, in the form of an XML file, and I want to create
> and populate a SAML AttributeStatement from the attributes and values in
> that XML file.  A couple of complications (to me):
> >
> > - The attribute names in the XML file are different than the
> corresponding attributes in the SAML AttributeStatement.
> > - Some of the attributes are multi-valued:  in the source XML file, the
> values are a colon-separated string (e.g. ab:cd:ef), but in the
> AttributeStatement, each value will be in separate <saml:Attribute>
> element.
> >
> > Here's an example source XML:
> >
> > <record>
> > .
> > .
> > .
> >  <adrRecord>
> >    <PN_ID>1111111</PN_ID>
> >    <personnel>
> >      <ADM_ORG_CD>urn:NORM:DEPT</ADM_ORG_CD>
> >      <DOD_ASSOC_CD>urn:NORM:V01</DOD_ASSOC_CD>
> >    </personnel>
> >  </adrRecord>
> > </record>
> >
> > and the AttributeStatement I want to transform the above to is something
> like:
> >
> >
> >    <saml:Attribute Name="MY_PN_ID"
> NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
> >        <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">1111111</saml:AttributeValue>
> >    </saml:Attribute>
> >
> >
> >    <saml:Attribute Name="MY_ORG_CD"
> NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
> >        <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">DEPT</saml:AttributeValue>
> >        <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">urn</saml:AttributeValue>
> >        <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">NORM</saml:AttributeValue>
> >    </saml:Attribute>
> >
> >
> >    <saml:Attribute Name="MY_ASSOC_CD"
> NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
> >        <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">urn</saml:AttributeValue>
> >        <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">NORM</saml:AttributeValue>
> >        <saml:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema";
> xsi:type="xsd:string">V01</saml:AttributeValue>
> >    </saml:Attribute>
> >
> > </saml:AttributeStatement>
> >
> > Can someone suggest the best approach to do this?  I actually would
> prefer to have a series of transforms to do this, rather than just a single
> XSLT, mainly so I can be able to see how things are working (or not
> working).
> >
> > Thanks,
> > Jim
> >
> >
>
>
> XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list>
> EasyUnsubscribe <http://lists.mulberrytech.com/unsub/xsl-list/3475705>(by
> email)
> XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list>
> EasyUnsubscribe <http://lists.mulberrytech.com/unsub/xsl-list/174322> (by
> email <>)
>


-- 
...Wendell Piez... ...wendell -at- nist -dot- gov...
...wendellpiez.com... ...pellucidliterature.org... ...pausepress.org...
...github.com/wendellpiez... ...gitlab.coko.foundation/wendell...

Current Thread