Error message 'adc' is an undeclared namespace - xslt-2.0

All, am im relatively new to XSLT but did some solutions allready. Currently i am trying to write an xslt that moves a file to different folders based on if
<adc:Status>uploaded</adc:Status>
is "uploaded" or not.
Despite declasring the xmlns:adc Namespace i get the error
Exception Occured during TransForm using XSLT 2.0 (SaxonHe):
System.Xml.XmlException: 'adc' is an undeclared namespace. Line 2,
position 4.
I spent hours looking for the error messages, trying to getmyself into namespaces, but currently i cant seem to find out whats the cause of the error.
This is the xslt i wrote
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:adc="http://www.ifra.com/adconnexion/#v2"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs xsi adc">
<xsl:output method="xml" encoding="ISO-8859-1" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="/">
<xsl:variable name="Filename">
<xsl:value-of select="replace(document-uri(.), '.*/', '')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="/adc:adConnexion/adc:Requests/adc:AdOrder/adc:ProductionDetail/adc:Material/adc:Status='uploaded'">
<!-- Business case -->
<xsl:variable name="OutputFileName" select="concat('file:///C:\UPLOADED\',$Filename)"/>
<xsl:result-document href="{$OutputFileName}">
<xsl:copy-of select="current()"/>
</xsl:result-document>
</xsl:when>
<xsl:otherwise>
<!-- Non Business case -->
<xsl:variable name="OutputFileName" select="concat('file:///C:\ELSE\',$Filename)"/>
<xsl:result-document href="{$OutputFileName}">
<xsl:copy-of select="current()"/>
</xsl:result-document>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The simplified inputfile is
<adc:adConnexion xmlns:adc="http://www.ifra.com/adconnexion/#v2">
<adc:Requests>
<adc:AdOrder messageClass="BusinessTransaction" messageID="FEA*" bookingID="1234" messageCode="AD-O">
<adc:ProductionDetail>
<adc:Material>
<adc:Status>uploaded</adc:Status>
</adc:Material>
</adc:AdOrder>
</adc:Requests>
</adc:adConnexion>
Any help is greatly appreciated!

Related

How to display latest date from N number of month in xslt?

I need to display latest date in N number of months using xslt.
My input:
2016/10/18
2016//10/15
2016/09/29
2016/09/15
and so on.
My output should be like below:
2016/10/18
2016/09/29
Can anyone help me on this?
Given a string of dates in that format you first need to tokenize to extract the date values, then you need to convert to the xs:date format, then you can group by the month and select the maximum value in each group. Using XSLT 3.0 that can be done as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:param name="input" as="xs:string">2016/10/18 2016/10/15 2016/09/29 2016/09/15</xsl:param>
<xsl:variable name="dates" as="xs:date*"
select="tokenize($input, '\s+')!xs:date(replace(., '/', '-'))"/>
<xsl:variable name="max-dates" as="xs:date*">
<xsl:for-each-group select="$dates" group-by="month-from-date(.)">
<xsl:sort select="current-grouping-key()"/>
<xsl:sequence select="max(current-group())"/>
</xsl:for-each-group>
</xsl:variable>
<xsl:template name="main" match="/">
<xsl:value-of select="$max-dates" separator="
"/>
</xsl:template>
</xsl:stylesheet>
In XSLT 2.0 you need to rewrite the date sequence construction a bit:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="2.0">
<xsl:param name="input" as="xs:string">2016/10/18 2016/10/15 2016/09/29 2016/09/15</xsl:param>
<xsl:variable name="dates" as="xs:date*"
select="for $dateString in tokenize($input, '\s+') return xs:date(replace($dateString, '/', '-'))"/>
<xsl:variable name="max-dates" as="xs:date*">
<xsl:for-each-group select="$dates" group-by="month-from-date(.)">
<xsl:sort select="current-grouping-key()"/>
<xsl:sequence select="max(current-group())"/>
</xsl:for-each-group>
</xsl:variable>
<xsl:template name="main" match="/">
<xsl:value-of select="$max-dates" separator="
"/>
</xsl:template>
</xsl:stylesheet>
I. Here is a short XSLT 2.0 solution:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:for-each-group select="d" group-by="substring(.,6,2)">
<xsl:sequence select="current-group()[. eq max(current-group()/string())][1]"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (unordered and multi-year dates -- to make it more interesting):
<t>
<d>2016/10/15</d>
<d>2016/09/15</d>
<d>2016/10/18</d>
<d>2016/09/29</d>
<d>2017/09/17</d>
</t>
the wanted, correct result is produced:
<d>2016/10/18</d>
<d>2017/09/17</d>
II. If the date that has the same month's highest day is wanted -- regardless of the year, this transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:for-each-group select="d" group-by="substring(.,6,2)">
<xsl:sequence select=
"current-group()[substring(.,9,2) eq max(current-group()/substring(.,9,2))][1]"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the same XML document (above), the correct result is produced:
<d>2016/10/18</d>
<d>2016/09/29</d>
III. If the dates are given together as a string:
Just use the tokenize() standard XPath 2.0 fy=unction.
For example, the equivalent of the first transformation above becomes:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vDates"
select="'2016/10/15 2016/09/15 2016/10/18 2016/09/29 2017/09/17'"/>
<xsl:template match="/">
<xsl:for-each-group select="tokenize($vDates, '\s+')[.]" group-by="substring(.,6,2)">
<xsl:sequence select="max(current-group())"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

Dynamic document lookup

I’m trying to come up with a way to dynamically determine which XML document to use for lookups. I parse the input XML document and based on the value I would like to set the appropriate lookup document to use. Ideally I would to have $ LookupDoc set to the correct document to read. The code snippet I have below doesn’t work. I could switch to XSLT 3.0 if that makes it easier.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="table-lookup" match="Row" use="#Key1"/>
<xsl:variable name="LookupLTE" select="document('HuaweiLTE.xml')/Huawei"/>
<xsl:variable name="LookupHSPA" select="document('HuaweiHSPA.xml')/Huawei"/>
<xsl:template match="measCollecFile/measData">
<xsl:variable name="DeviceName" select="#userLabel"/>
<xsl:choose>
<xsl:when test="substring($DeviceName,1,1)='L'">
<xsl:variable name="LookupDoc" select="$LookupLTE"/>
</xsl:when>
<xsl:when test="substring($DeviceName,1,1)='H'">
<xsl:variable name="LookupDoc" select="$LookupHSPA"/>
</xsl:when>
</xsl:choose>
<root>
<xsl:for-each select="measInfo">
<xsl:call-template name="loop"> </xsl:call-template>
</xsl:for-each>
</root>
</xsl:template>
You have not shown any use of the variable named LookupDoc you seem to want to define but I think you can simply use <xsl:variable name="LookupDoc" select="if (substring($DeviceName,1,1)='L') then $LookupLTE else if (substring($DeviceName,1,1)='H') then $LookupHSPA else ()"/> to define the variable.

XSLT set directory where result document ends up

The XSLT below creates result-documents as desired, with one exception: the result document ends up in the directory where the stylesheet was invoked from. I want the result document to be where it was found (i.e. overwrite itself with the transform version).
How can I do that?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" xpath-default-namespace="http://www.w3.org/1999/xhtml">
<xsl:template match="/">
<xsl:for-each select="collection(iri-to-uri('file:///home/paul/Text/?select=*.xhtml'))">
<xsl:variable name="filename">
<xsl:value-of select="tokenize(document-uri(.), '/')[last()]"/>
</xsl:variable>
<xsl:result-document indent="yes" method="xml" href="{$filename}">
<xsl:apply-templates/>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- transform templates removed -->
</xsl:stylesheet>
Try just using href="{document-uri(.)}" to use the full uri as the target rather than doing the tokenize to pull out the last segment.

XSLT mapping and summing source children into a single target attribute

I have a source document with XML structure similar to this:
<FOO>
<BAR>x</BAR>
<BAR>y</BAR>
<BAR>z</BAR>
</FOO>
My target XML must have an attribute with a number that represents the numerical sum of x, y and z. Where x = 1, y = 2 and z = 3
NOTE: The x, y and z are not actually numbers in the source document. They are letters and need to be mapped to the numbers that they represent first.
In this case, the target should look something like:
<Target Sum=6>
</Target>
Anyone have an XSLT example that would do what I need?
Thanks in advance
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Target Sum="{sum(FOO/BAR)}" />
</xsl:template>
EDIT:
This is a bit verbose, and there is probably a more elegant way to do it, but essentially I have a named template here that recursively calls itself to calculate the sum after the hard-coded mapping occurs:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Target>
<xsl:attribute name="Sum">
<xsl:call-template name="GetSum">
<xsl:with-param name="CurrentNode" select="FOO/BAR[position()=1]"/>
</xsl:call-template>
</xsl:attribute>
</Target>
</xsl:template>
<xsl:template name="GetSum">
<xsl:param name="CurrentNode"/>
<xsl:param name="Number" select="0"/>
<xsl:variable name="Recursive_Result">
<xsl:variable name="MappedNumber">
<xsl:choose>
<xsl:when test="$CurrentNode/. = 'x'">1</xsl:when>
<xsl:when test="$CurrentNode/. = 'y'">2</xsl:when>
<xsl:when test="$CurrentNode/. = 'z'">3</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- if there is a next sibling, recursively call GetSum -->
<xsl:choose>
<xsl:when test="$CurrentNode/following-sibling::BAR[1]">
<xsl:call-template name="GetSum">
<xsl:with-param name="CurrentNode" select="$CurrentNode/following-sibling::BAR[1]"/>
<xsl:with-param name="Number">
<xsl:value-of select="$MappedNumber"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$MappedNumber"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- add the recursive_result to the number passed into the template. this will eventually build a sum -->
<xsl:value-of select="$Recursive_Result + $Number"/>
</xsl:template>
</xsl:stylesheet>
A much simpler, shorter and efficient solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kMap" match="#value" use="../#key"/>
<xsl:variable name="vMaps" as="element()*">
<map key="x" value="1"/>
<map key="y" value="2"/>
<map key="z" value="3"/>
</xsl:variable>
<xsl:template match="/*">
<Target Sum="{sum(key('kMap', BAR, document('')))}"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<FOO>
<BAR>x</BAR>
<BAR>y</BAR>
<BAR>z</BAR>
</FOO>
the wanted, correct result is produced:
<Target Sum="6"/>
Explanation: Appropriate use of xsl:key, the 3rd argument of the key() function and AVT.

XSLT conditionally write to two different files

I need to extract log meesages from an XML file and write them out to plain text files. The log messages come in two flavors, and I want to write them to separate files.
I have written a style sheet that does exactly what I need except that it sometimes creates empty files because the XML file may not contain messages of one type or another.
I am wondering, 1) if what I ma doing is the best method to do this, and 2) if there is a way to suppress empty files.
My sample may contain errors because it has been retyped. (the original is on a closed network)
Note: I am using XSLT 2.0 features.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" encoding="iso-8859-1" />
<xsl:param name="break" select="string('
')" />
<xs:template match="/">
<xsl:result-document method="text" href="foo.txt">
<xsl:apply-templates select="Root/a/b/c[contains(., 'foo')]" />
</xsl:reult-document>
<xsl:result-document method="text" href="bar.txt">
<xsl:apply-templates select="Root/a/b/c[not(contains(., 'foo'))]" />
</xsl:reult-document>
</xsl:template>
<xsl:template match="*">
<xsl:value-of select=concat(normalize-space(.), $break)" />
</xsl:template>
</xsl:stylesheet>
You could use some XSLT 2.0 stylesheet like:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="break" select="string('
')" />
<xsl:template match="/">
<xsl:apply-templates select="Root/a/b/c"/>
</xsl:template>
<xsl:template match="/Root/a/b/c[contains(., 'foo')]">
<xsl:result-document method="text" href="foo.txt">
<xsl:next-match/>
</xsl:result-document>
</xsl:template>
<xsl:template match="/Root/a/b/c[not(contains(., 'foo'))]">
<xsl:result-document method="text" href="bar.txt">
<xsl:next-match/>
</xsl:result-document>
</xsl:template>
<xsl:template match="*">
<xsl:value-of select="concat(normalize-space(.), $break)" />
</xsl:template>
</xsl:stylesheet>
Note: Pattern matching and xsl:next-match.

Resources