XSLT Basics
XSLT
XSLT
Examples
Event Normalisation
Here is an example XSLT document that transforms XML data in the records:2
Namespace
Namespace
records:2 Namespace into XML in the event-logging:3 Namespace.event-logging:3 namespace.
It is an example of event normalisation from a bespoke format.
Warning
<?xml version="1.1" encoding="UTF-8" ?>
<xsl:stylesheet
xpath-default-namespace="records:2"
xmlns="event-logging:3"
xmlns:stroom="stroom"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.0">
<!-- Match the root element -->
<xsl:template match="records">
<Events
xsi:schemaLocation="event-logging:3 file://event-logging-v3.0.0.xsd"
Version="3.0.0">
<xsl:apply-templates />
</Events>
</xsl:template>
<!-- Match each 'record' element -->
<xsl:template match="record">
<xsl:variable name="user" select="data[@name='User']/@value" />
<Event>
<xsl:call-template name="header" />
<xsl:value-of select="stroom:log('info', concat('Processing user: ', $user))"/>
<EventDetail>
<TypeId>0001</TypeId>
<Description>
<xsl:value-of select="data[@name='Message']/@value" />
</Description>
<Authenticate>
<Action>Logon</Action>
<LogonType>Interactive</LogonType>
<User>
<Id>
<xsl:value-of select="$user" />
</Id>
</User>
<Data Name="FileNo">
<xsl:attribute name="Value" select="data[@name='FileNo']/@value" />
</Data>
<Data Name="LineNo">
<xsl:attribute name="Value" select="data[@name='LineNo']/@value" />
</Data>
</Authenticate>
</EventDetail>
</Event>
</xsl:template>
<xsl:template name="header">
<xsl:variable name="date" select="data[@name='Date']/@value" />
<xsl:variable name="time" select="data[@name='Time']/@value" />
<xsl:variable name="dateTime" select="concat($date, $time)" />
<xsl:variable name="formattedDateTime" select="stroom:format-date($dateTime, 'dd/MM/yyyyHH:mm:ss')" />
<xsl:variable name="user" select="data[@name='User']/@value" />
<EventTime>
<TimeCreated>
<xsl:value-of select="$formattedDateTime" />
</TimeCreated>
</EventTime>
<EventSource>
<System>
<Name>Test</Name>
<Environment>Test</Environment>
</System>
<Generator>CSV</Generator>
<Device>
<IPAddress>1.1.1.1</IPAddress>
<MACAddress>00-00-00-00-00-00</MACAddress>
<xsl:variable name="location" select="stroom:lookup('FILENO_TO_LOCATION_MAP', data[@name='FileNo']/@value, $formattedDateTime)" />
<xsl:if test="$location">
<xsl:copy-of select="$location" />
</xsl:if>
<Data Name="Zone1">
<xsl:attribute name="Value" select="stroom:lookup('IPToLocation', stroom:numeric-ip('192.168.1.1'))" />
</Data>
</Device>
<User>
<Id>
<xsl:value-of select="$user" />
</Id>
</User>
</EventSource>
</xsl:template>
</xsl:stylesheet>
Reference Data
Here is an example of transforming Reference Data in the records:2
Namespace
Namespace
records:2 Namespace into XML in the event-logging:3 Namespace.reference-data:2 namespace that is suitable for loading using the
ReferenceDataFilter
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet
xpath-default-namespace="records:2"
xmlns="reference-data:2"
xmlns:evt="event-logging:3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="records">
<referenceData
xsi:schemaLocation="reference-data:2 file://reference-data-v2.0.1.xsd event-logging:3 file://event-logging-v3.0.0.xsd"
version="2.0.1">
<xsl:apply-templates/>
</referenceData>
</xsl:template>
<xsl:template match="record">
<reference>
<map>USER_ID_TO_STAFF_NO_MAP</map>
<key><xsl:value-of select="data[@name='userId']/@value"/></key>
<value><xsl:value-of select="data[@name='staffNo']/@value"/></value>
</reference>
</xsl:template>
</xsl:stylesheet>
Identity Transformation
If you want an XSLT to decorate an Events XML document with some additional data or to change it slightly without changing its namespace then a good starting point is the identity transformation.
<xsl:stylesheet
version="1.0"
xpath-default-namespace="event-logging:3"
xmlns="event-logging:3"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Match Root Object -->
<xsl:template match="Events">
<Events
xmlns="event-logging:3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="event-logging:3 file://event-logging-v3.4.2.xsd"
Version="3.4.2">
<xsl:apply-templates />
</Events>
</xsl:template>
<!-- Whenever you match any node or any attribute -->
<xsl:template match="node( )|@*">
<!-- Copy the current node -->
<xsl:copy>
<!-- Including any attributes it has and any child nodes -->
<xsl:apply-templates select="@*|node( )" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This XSLT will copy every node and attribute as they are, returning the input document completely un-changed.
You can then add additional templates to match on specific elements and modify them, for example decorating a user’s UserDetails elements with value obtained from a reference data lookup on a user ID.
Note
<xsl:message>
Stroom supports the standard The log() function writes a message to the processing log with the specified severity.
Severities of INFO, WARN, ERROR and FATAL can be used.
Severities of ERROR and FATAL will result in records being omitted from the output if a
RecordOutputFilter is used in the pipeline. The counts for RecWarn, RecError will be affected by warnings or errors generated in
this way therefore this function is useful for adding business rules to XML output.<xsl:message> element from the
http://www.w3.org/1999/XSL/Transform
.
This element behaves in a similar way to the
stroom:log()
stroom:log()ERROR.
A child element can optionally be used to set the severity level (one of FATAL|ERROR|WARN|INFO).
The namespace of this element does not matter.
You can also set the attribute terminate="yes" to log the message at severity FATAL and halt processing of that stream part.
If the stream is multi-part then processing will continue with the next part.
Note
terminate="yes" will trump any severity defined by a child element.
It will always be logged at FATAL.
The following are some examples of using <xsl:message>.
<!-- Log a message using default severity of ERROR -->
<xsl:message>Invalid length</xsl:message>
<!-- terminate="yes" means log the message as a FATAL ERROR and halt processing of the stream part -->
<xsl:message terminate="yes">Invalid length</xsl:message>
<!-- Log a message with a child element name specifying the severity. -->
<xsl:message>
<warn>Invalid length</warn>
</xsl:message>
<!-- Log a message with a child element name specifying the severity. -->
<xsl:message>
<info>Invalid length</info>
</xsl:message>
<!-- Log a message, specifying the severity and using a dynamic value. -->
<xsl:message>
<info>
<xsl:value-of select="concat('User ID ', $userId, ' is invalid')" />
</info>
</xsl:message>