What is wrong with the piece of XSLT code - xslt-1.0

Just started coding. Attribute value setting does not seem to work and the child nodes of the reference node in the outer for-each loop are not accessed. I am not able to figure out what is wrong with the code.
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<B2BGatewayDoc>
<Document>
<xsl:copy-of select="/B2BGatewayDoc/Document/ServiceHeader" />
<ServiceContent>
<Transmission>
<TransmissionBody>
<xsl:copy-of select="/B2BGatewayDoc/Document/ServiceContent/Transmission/TransmissionBody/ReceiverSystem" />
<xsl:for-each select="/B2BGatewayDoc/Document/ServiceContent/Transmission/TransmissionBody/GLogXMLElement" >
<GLogXMLElement>
<ActualShipment>
<xsl:attribute name="xmlns" >
<xsl:value-of select="ActualShipment/#xmlns" />
</xsl:attribute>
<Shipment>
<xsl:copy-of select="ActualShipment/Shipment/ShipmentHeader" />
<xsl:for-each select="ActualShipment/Shipment/TransOrder" >
<TransOrder>
<xsl:copy-of select="TransOrderHeader" />
</TransOrder>
</xsl:for-each>
</Shipment>
</ActualShipment>
</GLogXMLElement>
</xsl:for-each>
</TransmissionBody>
</Transmission>
</ServiceContent>
</Document>
</B2BGatewayDoc>
</xsl:template>
</xsl:stylesheet>
XML Sample
<?xml version="1.0"?>
<B2BGatewayDoc>
<Document>
<ServiceHeader>
<Protocol name="ANSIX12">
<Sender>941714834TEST</Sender>
<Receiver>003897733SAPT</Receiver>
<MsgType>3A4MG2</MsgType>
<ReferenceNumber/>
<PipVersion>V02.02</PipVersion>
<InstanceIdentifier/>
</Protocol>
</ServiceHeader>
<ServiceContent>
<Transmission>
<TransmissionBody>
<ReceiverSystem>
<ReceiverSystemName>XXXXX</ReceiverSystemName>
<ReceiverSystemName>YYYYY</ReceiverSystemName>
</ReceiverSystem>
<GLogXMLElement>
<ActualShipment xmlns="1000181ZZZ">
<Shipment>
<ShipmentHeader>
<ShipmentGid>
<Gid>
<Xid>1000181ZZZ</Xid>
</Gid>
</ShipmentGid>
<ShipmentRefnum>
<ShipmentRefnumQualifierGid>
<Gid>
<Xid>SELLER_INV_NBR</Xid>
</Gid>
</ShipmentRefnumQualifierGid>
<ShipmentRefnumValue>VALUE 1</ShipmentRefnumValue>
</ShipmentRefnum>
<ShipmentRefnum>
<ShipmentRefnumQualifierGid>
<Gid>
<Xid>SHIP_TYPE</Xid>
</Gid>
</ShipmentRefnumQualifierGid>
<ShipmentRefnumValue>O</ShipmentRefnumValue>
</ShipmentRefnum>
<TransactionCode>IU</TransactionCode>
<TotalShipUnitCount>1</TotalShipUnitCount>
<TotalPackagedItemSpecCount>1</TotalPackagedItemSpecCount>
<CommercialTerms>
<IncoTermGid>
<Gid>
<Xid>EXW</Xid>
</Gid>
</IncoTermGid>
</CommercialTerms>
<NumOrderReleases>1</NumOrderReleases>
<VesselGid>
<Gid>
<Xid>BOL54891</Xid>
</Gid>
</VesselGid>
</ShipmentHeader>
<TransOrder>
<TransOrderHeader>
<TransOrderGid>
<Gid>
<Xid>BR20000238</Xid>
</Gid>
</TransOrderGid>
<CommercialTerms>
<IncoTermGid>
<Gid>
<Xid>EXW</Xid>
</Gid>
</IncoTermGid>
</CommercialTerms>
<TransportModeGid>
<Gid>
<Xid>H</Xid>
</Gid>
</TransportModeGid>
<ModeProfileGid>
<Gid>
<Xid/>
</Gid>
</ModeProfileGid>
<OrderRefnum>
<OrderRefnumQualifierGid>
<Gid>
<Xid>BOL</Xid>
</Gid>
</OrderRefnumQualifierGid>
<OrderRefnumValue>BOL54891</OrderRefnumValue>
</OrderRefnum>
<OrderRefnum>
<OrderRefnumQualifierGid>
<Gid>
<Xid>CUST_PO_NBR</Xid>
</Gid>
</OrderRefnumQualifierGid>
<OrderRefnumValue>4700175047</OrderRefnumValue>
</OrderRefnum>
<TotalNetWeightVolume>
<Weight>
<WeightValue>18.9</WeightValue>
<WeightUOMGid>
<Gid>
<Xid>KG</Xid>
</Gid>
</WeightUOMGid>
</Weight>
<Volume>
<VolumeValue>0</VolumeValue>
<VolumeUOMGid>
<Gid>
<Xid>CR</Xid>
</Gid>
</VolumeUOMGid>
</Volume>
</TotalNetWeightVolume>
</TransOrderHeader>
</TransOrder>
</Shipment>
</ActualShipment>
</GLogXMLElement>
</TransmissionBody>
</Transmission>
</ServiceContent>
</Document>
</B2BGatewayDoc>
The output I get
<?xml version='1.0' ?>
<B2BGatewayDoc>
<Document>
<ServiceHeader>
<Protocol name="ANSIX12">
<Sender>941714834TEST</Sender>
<Receiver>003897733SAPT</Receiver>
<MsgType>3A4MG2</MsgType>
<ReferenceNumber/>
<PipVersion>V02.02</PipVersion>
<InstanceIdentifier/>
</Protocol>
</ServiceHeader>
<ServiceContent>
<Transmission>
<TransmissionBody>
<ReceiverSystem>
<ReceiverSystemName>XXXXX</ReceiverSystemName>
<ReceiverSystemName>YYYYY</ReceiverSystemName>
</ReceiverSystem>
<GLogXMLElement>
<ActualShipment xmlns="">
<Shipment/>
</ActualShipment>
</GLogXMLElement>
</TransmissionBody>
</Transmission>
</ServiceContent>
</Document>
</B2BGatewayDoc>
Attribute xmlns is not assigned a value though the source value is availale. All nodes and tags within tag Shipment do not get created.
Any pointer to reason is highly appreciated.

For the formal error, Visual Studio says, An attribute with a local name 'xmlns' and a null namespace URI cannot be created. Which makes sense - you don't create namespaces like this.
Even if you could dynamically generate a namespace for your output, it would still not work because your XSL template would not be able to query the data file. In your data you have <ActualShipment xmlns="1000181ZZZ">, so all nodes under ActualShipment have the namespace 1000181ZZZ, and you have to put that namespace into your XSL. Otherwise the XSL looks for nodes with a blank namespace and does not find any -- all nodes have a namespace of 1000181ZZZ. To query that from an XSL, you would need to add xmlns:something="1000181ZZZ" to the declaration of your <xsl:stylesheet>:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:something="1000181ZZZ">
and then use it in the xPath like this:
<Shipment>
<xsl:copy-of select="something:ActualShipment/something:Shipment/something:ShipmentHeader" />
<xsl:for-each select="something:ActualShipment/something:Shipment/something:TransOrder" >
<TransOrder>
<xsl:copy-of select="something:TransOrderHeader" />
</TransOrder>
</xsl:for-each>
</Shipment>
You shouldn't be using namespaces like variables. They are supposed to be known in advance.
An obvious fix is to replace <ActualShipment xmlns="1000181ZZZ"> with something like <ActualShipment some_attribute="1000181ZZZ">.

Related

Can someone explain this 'Get all Numeric Elements' operator Predicate Filter?

Trying to get elements with a value that is numeric, including zero.
sample xml
<?xml version="1.0" encoding="utf-8"?>
<PRODUCTS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PRODUCT>
<REFERENCE>10</REFERENCE>
<ATTRIBUTES />
<TITLE>fdg</TITLE>
<ACTIVE>1</ACTIVE>
<DELETE>0</DELETE>
<STOCK>10</STOCK>
<WEIGHT>0.00</WEIGHT>
<MODEL>f</MODEL>
<EAN />
<MPN />
<ISBN />
<UPC />
<PRICE>10.000</PRICE>
<SALE_PRICE>10.000</SALE_PRICE>
<RRP_PRICE>0.000</RRP_PRICE>
<COST_PRICE>0.000</COST_PRICE>
<VAT_RATE>0.00</VAT_RATE>
</PRODUCT>
</PRODUCTS>
Expected Output
<?xml version="1.0" encoding="utf-8"?>
<Values>
<REFERENCE>10</REFERENCE>
<ACTIVE>1</ACTIVE>
<DELETE>0</DELETE>
<STOCK>10</STOCK>
<WEIGHT>0.00</WEIGHT>
<PRICE>10.000</PRICE>
<SALE_PRICE>10.000</SALE_PRICE>
<RRP_PRICE>0.000</RRP_PRICE>
<COST_PRICE>0.000</COST_PRICE>
<VAT_RATE>0.00</VAT_RATE>
</Values>
'all numeric' [number()] returns 0 as single predicate expression
<xsl:template match="/">
<Value>
<xsl:for-each select="//*[number()]">
<xsl:element name="{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
Excluding elements without descendants produces expected (non-zero) numbers
<xsl:for-each select="//*[number() and not(descendant::*)]">
Get all 0 using *[number() or format-number(text(),0)='0']
non-zero numbers
<xsl:for-each select="//*[number() and not(descendant::*)]">
all numbers
<xsl:for-each select="//*[number() or format-number(text(),0)='0']">
Adding an additional and or or seems to produce the expected.
Why does select="*[number()]" output 0 results, until an and/or is added?
The rules for evaluating the expression in a predicate are as follows:
If the expression evaluates to a number $n, then the result is true if $n is equal to the context position; IOW, it is as if you have written [position() = $n];
Otherwise the expression is evaluated as a boolean.
To demonstrate, consider the following simplified example:
XML
<items>
<item>0</item>
<item>x</item>
<item>3</item>
<item>y</item>
<item>4</item>
</items>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/items">
<xsl:copy>
<xsl:copy-of select="item[number()]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>3</item>
</items>
Here only the item whose value, when converted to a number, matches the item's position was copied.
If instead you do:
<xsl:copy-of select="item[number() or false()]"/>
then the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>3</item>
<item>4</item>
</items>
because now the expression is evaluated as a boolean and any number other than 0 will be evaluated as true.
In order to copy all items with a "numeric" value, you need to do:
<xsl:copy-of select="item[number() = number()]"/>
to get:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>0</item>
<item>3</item>
<item>4</item>
</items>
This excludes the items whose value cannot be converted to a number - because NaN is not equal to anything, including itself.
When you evaluate number() in a predicate, it is behaving as if you had specified the short-hand for the position. i.e. for <REFERENCE>10</REFERENCE> it was as if you had tested //*[10] or the longer form //*[position()=10] and that element is at position 1 and not 10, so it didn't select any elements.
Another way to evaluate a boolean expression and test if they are numeric would be to use:
[number() eq number()]

XSLT error when adding accumulator logic to Web-service Request

I am creating an integration to load supplier invoices with multiple lines to workday after getting the details from an input file. Everything seems to be working as expected till i add accumulator logic in web service request in XSLT to first check if the cost center exists in workday. If it does then i populate that else i need to populate a default value. I have not built the default logic if cost center does not exist in the map yet.
I have created a custom report get all active cost center and i merge this list with my input file.
Sample data after i merge my custom report with input file -
<?xml version="1.0" encoding="utf-8"?>
<Header>
<wd:Report_Data xmlns:wd="urn:com.workday.report/INT1007a_CR_PRC_FEDEX_Supplier_Invoices_Cost_Center">
<wd:Report_Entry>
<wd:CostCenter1>40720</wd:CostCenter1>
<wd:CostCenter2>40720</wd:CostCenter2>
</wd:Report_Entry>
</wd:Report_Data>
<root>
<row>
<col1>199779087</col1>
<col2>652233142</col2>
<col3>20190416</col3>
<col4>O</col4>
<col5>D</col5>
<col6>000000000002256</col6>
<col7>00002</col7>
<col8>019153223</col8>
<col9>US</col9>
<col10> </col10>
<col11> </col11>
<col12>774894489995</col12>
<col13>2</col13>
<col14>20190406</col14>
<col15>05</col15>
<col16>06</col16>
<col17>40720/PHL </col17>
<col18>1</col18>
<col19>000000000001128</col19>
<col20>USD</col20>
<col21>050</col21>
<col22>000000000002394</col22>
<col23>+</col23>
<col24>185</col24>
<col25>000000000001345</col25>
<col26>-</col26>
<col27>010</col27>
<col28>000000000000079</col28>
<col29>+</col29>
<col30> </col30>
<col31>000000000000000</col31>
<col32> </col32>
<col33> </col33>
<col34>000000000000000</col34>
<col35> </col35>
<col36> </col36>
<col37>000000000000000</col37>
<col38> </col38>
<col39> </col39>
<col40>000000000000000</col40>
<col41> </col41>
<col42>000000000</col42>
<col43>200 </col43>
<col44>00001</col44>
<col45>0000000</col45>
<col46>0000000</col46>
<col47>L</col47>
<col48>Test Test </col48>
<col49>Test </col49>
<col50>40720/PHL </col50>
<col51>Test </col51>
<col52> </col52>
<col53>Test </col53>
<col54>PA</col54>
<col55>19103 </col55>
<col56>US</col56>
<col57>02</col57>
<col58>Irshad Khan </col58>
<col59>FINRA </col59>
<col60>9513 Key West Avenue </col60>
<col61> </col61>
<col62>ROCKVILLE </col62>
<col63>MD</col63>
<col64>20850 </col64>
<col65>US</col65>
<col66>20190408</col66>
<col67>0836</col67>
<col68> </col68>
<col69>00000000</col69>
<col70>0000</col70>
<col71>A.WANG </col71>
<col72>A1</col72>
<col73>000000000000000</col73>
<col74>USD</col74>
<col75> </col75>
<col76> </col76>
<col77> </col77>
<col78> </col78>
<col79> </col79>
<col80> </col80>
<col81>00000000</col81>
<col82> </col82>
<col83> </col83>
<col84>000000001000000000</col84>
<col85>USD</col85>
<col86>40720/PHL </col86>
<col87> </col87>
</row>
</root>
</Header>
Below is my working XSLT i.e before adding accumulator logic -
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:wd="urn:com.workday/bsvc"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wd1="urn:com.workday.report/INT1007a_CR_PRC_FEDEX_Supplier_Invoices_Cost_Center"
exclude-result-prefixes="wd xsl xsd"
version="3.0">
<xsl:output method="xml"/>
<xsl:param name="fpCompanyRefID"/>
<xsl:param name="fpSupplierRefID"/>
<xsl:param name="fpPaymentTermsRefID"/>
<xsl:param name="fpSpendCategoryRefID"/>
<xsl:param name="fpUOMRefID"/>
<xsl:param name="fpWebServiceVersion"/>
<xsl:param name="fpEventID"/>
<xsl:template match="/Header">
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<env:Body>
<!--Group by Invoice Number to process all lines for an invoice-->
<xsl:for-each-group select="root/row" group-by="col2">
<wd:Submit_Supplier_Invoice_Request xmlns:wd="urn:com.workday/bsvc" wd:Add_Only="true">
<xsl:attribute name="wd:version" select="$fpWebServiceVersion"/>
<wd:Business_Process_Parameters>
<!--When set to "true",all required approvals will be automatically marked as approved.-->
<!--<wd:Auto_Complete>true</wd:Auto_Complete> -->
<wd:Comment_Data>
<wd:Comment>
<xsl:value-of select="concat('Integration Event WID : ',$fpEventID)"/>
</wd:Comment>
</wd:Comment_Data>
</wd:Business_Process_Parameters>
<wd:Supplier_Invoice_Data>
<!--Need to create Supplier Invoice in draft status. Therefore did not set this element.-->
<!--<wd:Submit>true</wd:Submit>-->
<wd:Company_Reference>
<wd:ID wd:type="Company_Reference_ID">
<xsl:value-of select="$fpCompanyRefID"/>
</wd:ID>
</wd:Company_Reference>
<wd:Supplier_Reference>
<wd:ID wd:type="Supplier_ID">
<xsl:value-of select="$fpSupplierRefID"/>
</wd:ID>
</wd:Supplier_Reference>
<wd:Invoice_Date><xsl:value-of select="format-date(col3,'[Y0001]-[M01]-[D01]')"/></wd:Invoice_Date>
<wd:Invoice_Date>
<xsl:value-of select="concat(substring(col3, 1, 4),'-',substring(col3,5, 2),'-',substring(col3, 7, 2))"/>
</wd:Invoice_Date>
<wd:Invoice_Received_Date>
<!-- <xsl:value-of select="format-date(current-date(),'[Y0001]-[M01]-[D01]')"/> -->
</wd:Invoice_Received_Date>
<wd:Control_Amount_Total>
<xsl:value-of select="col6 * 0.01"/>
</wd:Control_Amount_Total>
<!--<wd:Withholding_Tax_Amount><xsl:value-of select="col2"/></wd:Withholding_Tax_Amount>-->
<wd:Suppliers_Invoice_Number>
<xsl:value-of select="col2"/>
</wd:Suppliers_Invoice_Number>
<wd:Memo><xsl:value-of select="col8"/></wd:Memo>
<wd:Payment_Terms_Reference>
<wd:ID wd:type="Payment_Terms_ID">
<xsl:value-of select="$fpPaymentTermsRefID"/>
</wd:ID>
</wd:Payment_Terms_Reference>
<wd:Gross_Invoice_Amount>
<xsl:value-of select="col6 * 0.01"/>
</wd:Gross_Invoice_Amount>
<!-- Process all the lines for the group. Grouping is done at the header level by Invoice Number-->
<xsl:for-each select="current-group()">
<wd:Invoice_Line_Replacement_Data>
<wd:Item_Description>
<xsl:value-of select="col12"/>
</wd:Item_Description>
<wd:Spend_Category_Reference>
<wd:ID wd:type="Spend_Category_ID">
<xsl:value-of select="$fpSpendCategoryRefID"/>
</wd:ID>
</wd:Spend_Category_Reference>
<wd:Worktags_Reference>
<wd:ID wd:type="Cost_Center_Reference_ID">
55075
</wd:ID>
</wd:Worktags_Reference>
<wd:Worktags_Reference>
<wd:ID wd:type="Project_ID">3333</wd:ID>
</wd:Worktags_Reference>
<wd:Worktags_Reference>
<wd:ID wd:type="Location_ID">LOC_KWB</wd:ID>
</wd:Worktags_Reference>
<wd:Quantity>1</wd:Quantity>
<wd:Unit_Cost>
<xsl:value-of select="col19 * 0.01"/>
</wd:Unit_Cost>
<wd:Unit_of_Measure_Reference>
<wd:ID wd:type="UN_CEFACT_Common_Code_ID">
<xsl:value-of select="$fpUOMRefID"/>
</wd:ID>
</wd:Unit_of_Measure_Reference>
<wd:Extended_Amount>
<xsl:value-of select="col19 * 0.01"/>
</wd:Extended_Amount>
<wd:Memo><xsl:value-of select="concat('Sender Name: ',col48,' / Receiver Name: ', col58)"/></wd:Memo>
</wd:Invoice_Line_Replacement_Data>
</xsl:for-each>
</wd:Supplier_Invoice_Data>
</wd:Submit_Supplier_Invoice_Request>
</xsl:for-each-group>
</env:Body>
</env:Envelope>
</xsl:template>
</xsl:stylesheet>
But the moment I add below accumulator logic to above XSLT, Studio error out
I am adding below code after -
<xsl:mode streamable="yes" on-no-match="shallow-skip" use-accumulators="CostCenterLookup CurrentLookupValue"/>
<xsl:output method="xml"/>
<xsl:accumulator name="CurrentLookupValue" as="xs:string" initial-value="''" streamable="yes">
<xsl:accumulator-rule match="wd:CostCenter1/text()" select="."/>
</xsl:accumulator>
<xsl:accumulator name="CostCenterLookup" as="map(xs:string,xs:string)" initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="wd:CostCenter2/text()" select="map:put($value, accumulator-before('CurrentLookupValue'), string(.))"/>
</xsl:accumulator>
I am adding below code for getting value for Cost_Center_Reference_ID -
<xsl:value-of select="accumulator-before('CostCenterLookup')( normalize-space( substring(col17,1,5) ) )"/>
Error that I am getting is not very helpful to debug the exact issue -
Submit Supplier Invoice Request Failed for null. Error during invocation: null
Would really appreciate some help on this.
I would guess the error is simply caused by trying to use streaming (as you declare that on the xsl:mode) and xsl:for-each-group select="root/row" group-by="col2". With streaming you can't access child elements of the population elements (i.e. the row elements) in the group-by expression as that would require processing (consuming) the contents of each row. So you would need to use xsl:for-each-group select="root/row!copy-of()" group-by="col2". Otherwise the processor will reject the stylesheet code as not being streamable. You might want to try to use Saxon EE from the command line to verify, it for sure will give better diagnostics.

XSLT Outer Bracket Node Inside Inner Bracket

I have the following XML:
<Root>
<PersonSettings>
<Type Drinks="1">A</Type>
<Type Drinks="2">B</Type>
<Type Drinks="3">C</Type>
<LowestAge>20</LowestAge>
<MaxAge>49</MaxAge>
</PersonSettings>
<PersonSettings>
<Type Drinks="5">A</Type>
<Type Drinks="8">B</Type>
<Type Drinks="1">C</Type>
<LowestAge>50</LowestAge>
<MaxAge>90</MaxAge>
</PersonSettings>
<Person Alive="Yes">
<Type>A</Type>
<Age>23</Age>
</Person>
<Person Alive="Yes">
<Type>B</Type>
<Age>50</Age>
</Person>
<Person Alive="Yes">
<Type>C</Type>
<Age>51</Age>
</Person>
<Person Alive="Yes">
<Type>A</Type>
<Age>70</Age>
</Person>
</Root>
And the following XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="/">
<PersonsOneDrink>
<xsl:value-of select="(count(/Root/Person[#Alive = 'Yes' and /Root/PersonSettings[**(Person/Age)** >= LowestAge and **(Person/Age)** <= MaxAge]/Type[. = **(Person/Type)** and #Drinks = '1']))"/>
</PersonsOneDrink>
</xsl:template>
</xsl:stylesheet>
What I want to do in this example is count the number of people with one drink, that are alive. I have different settings depending on the Type of Person and the Age of such person. For example Type A has one drink if between 20 and 49 years of age, while type C has one drink if between 50 to 90 years. The problem I find is that I cannot reference the outer node when going inside a second set of brackets. Is there any way to create a variable on the fly?
P.S: The parts with * * () * * are where I would like to reference the outer node.
I don't think this can be done with a single count() expression, applied from the context of the root.
The problem here is that you need to refer to the values of Person within the predicate comparing them to the values of the prospective PersonSettings. For this, you need to use the current() function - but in order to have the current() function refer to Person, you must first make Person the context node.
Here is a suggested approach that writes a character for each Person that passes the test into a variable. This eliminates the need to convert the variable into a node-set before counting it:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Root">
<xsl:variable name="v1">
<xsl:for-each select="Person">
<xsl:if test="#Alive = 'Yes' and /Root/PersonSettings[LowestAge <= current()/Age and MaxAge >= current()/Age]/Type[. = current()/Type]/#Drinks = 1">
<xsl:text>Y</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<PersonsOneDrink>
<xsl:value-of select="string-length($v1)"/>
</PersonsOneDrink>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<PersonsOneDrink>2</PersonsOneDrink>
A more general solution - suitable if you want to perform several queries on the supplied data - would start by rewriting the input and assigning each Person the corresponding amount of drinks .
Here we will construct a variable that contains:
<Person>
<Type>A</Type>
<Age>23</Age>
<Drinks>1</Drinks>
</Person>
<Person>
<Type>B</Type>
<Age>50</Age>
<Drinks>8</Drinks>
</Person>
<Person>
<Type>C</Type>
<Age>51</Age>
<Drinks>1</Drinks>
</Person>
<Person>
<Type>A</Type>
<Age>70</Age>
<Drinks>5</Drinks>
</Person>
Then, once the variable is converted into a node-set, it is trivial to get the number of persons with any amount of drinks:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Root">
<!-- first pass -->
<xsl:variable name="persons-rtf">
<xsl:for-each select="Person">
<xsl:copy>
<xsl:copy-of select="*"/>
<Drinks>
<xsl:value-of select="/Root/PersonSettings[LowestAge <= current()/Age and MaxAge >= current()/Age]/Type[. = current()/Type]/#Drinks"/>
</Drinks>
</xsl:copy>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="persons" select="exsl:node-set($persons-rtf)/Person"/>
<!-- output -->
<results>
<PersonsOneDrink>
<xsl:value-of select="count($persons[Drinks = 1])"/>
</PersonsOneDrink>
</results>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0 to concatenate elements

I have a XML which has complex element called ExternalRequestIDs,
My requirement is to concatenate the ExternalRequestID values coming at run time.
If the input has 5 ExternalRequestID values then the 5 values needs to be concatenated.
The XSL i have created perform only static translation, am trying to accomplish this logic in xslt 1.0, i am new to xslt Please help
Source XML -
<?xml version="1.0" encoding="UTF-8" ?>
<RetrieveMIProcessRequest xmlns="http://xmlns.mycompany.com/RetrieveMI">
<ExternalRequestIDs>
<ExternalRequestID>ID1</ExternalRequestID>
</ExternalRequestIDs>
<ExternalRequestIDs>
<ExternalRequestID>ID2</ExternalRequestID>
</ExternalRequestIDs>
<ExternalRequestIDs>
<ExternalRequestID>ID3</ExternalRequestID>
</ExternalRequestIDs>
<SourceSystem>SourceSystemName</SourceSystem>
</RetrieveMIProcessRequest>
Transformation Created to Concatenate ExternalRequestID values -
<xsl:template match="/">
<ns0:RetrieveMIProcessRequest>
<xsl:for-each select="/ns0:RetrieveMIProcessRequest/ns0:ExternalRequestIDs">
<xsl:variable name="ExtId"
select="concat("'",/ns0:RetrieveMIProcessRequest/ns0:ExternalRequestIDs[1]/ns0:ExternalRequestID,"','",/ns0:RetrieveMIProcessRequest/ns0:ExternalRequestIDs[2]/ns0:ExternalRequestID"'")"/>
<ns0:ExternalRequestIDs>
<ns0:ExternalRequestID>
<xsl:value-of select="$ExtId"/>
</ns0:ExternalRequestID>
</ns0:ExternalRequestIDs>
</xsl:for-each>
<ns0:SourceSystem>
<xsl:value-of select="/ns0:RetrieveMIProcessRequest/ns0:SourceSystem"/>
</ns0:SourceSystem>
</ns0:RetrieveMIProcessRequest>
</xsl:template>
Output after transformation -
<?xml version = '1.0' encoding = 'UTF-8'?>
<ns0:RetrieveMIProcessRequest xmlns:ns0="http://xmlns.mycompany.com/RetrieveMI">
<ns0:ExternalRequestIDs>
<ns0:ExternalRequestID>'ID1','ID2'</ns0:ExternalRequestID>
</ns0:ExternalRequestIDs>
<ns0:ExternalRequestIDs>
<ns0:ExternalRequestID>'ID1','ID2'</ns0:ExternalRequestID>
</ns0:ExternalRequestIDs>
<ns0:ExternalRequestIDs>
<ns0:ExternalRequestID>'ID1','ID2'</ns0:ExternalRequestID>
</ns0:ExternalRequestIDs>
<ns0:SourceSystem>SourceSystemName</ns0:SourceSystem>
</ns0:RetrieveMIProcessRequest>
Expected Output -
<?xml version = '1.0' encoding = 'UTF-8'?>
<ns0:RetrieveMIProcessRequest xmlns:ns0="http://xmlns.mycompany.com/RetrieveMI">
<ns0:ExternalRequestIDs>
<ns0:ExternalRequestID>'ID1','ID2','ID3'</ns0:ExternalRequestID>
</ns0:ExternalRequestIDs>
<ns0:SourceSystem>SourceSystemName</ns0:SourceSystem>
</ns0:RetrieveMIProcessRequest>
This needs to be achieved at run based on the ExternalRequestID values coming in the input request.
If the input has 5 ExternalRequestID values then the values needs to be concatenated
OK, this template should fit your needs :
<xsl:template match="/">
<ns0:RetrieveMIProcessRequest>
<ns0:ExternalRequestIDs>
<ns0:ExternalRequestID>
<!-- Loop on all ExternalRequestID and concatenate the textual values -->
<xsl:for-each select="/ns0:RetrieveMIProcessRequest/ns0:ExternalRequestIDs/ns0:ExternalRequestID">
<xsl:text>'</xsl:text>
<xsl:value-of select="."/>
<xsl:text>'</xsl:text>
<xsl:if test="following::ns0:ExternalRequestID">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</ns0:ExternalRequestID>
</ns0:ExternalRequestIDs>
<ns0:SourceSystem>
<xsl:value-of select="/ns0:RetrieveMIProcessRequest/ns0:SourceSystem"/>
</ns0:SourceSystem>
</ns0:RetrieveMIProcessRequest>
</xsl:template>
The output I get is:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:RetrieveMIProcessRequest xmlns:ns0="http://xmlns.mycompany.com/RetrieveMI">
<ns0:ExternalRequestIDs>
<ns0:ExternalRequestID>'ID1','ID2','ID3'</ns0:ExternalRequestID>
</ns0:ExternalRequestIDs>
<ns0:SourceSystem>SourceSystemName</ns0:SourceSystem>
</ns0:RetrieveMIProcessRequest>

Need to group xsl:for-each-group with Muenchian method

I am not able to retrieve unique list by applying Muenchian method. I am trying to group based on "Series Title" attribute
Sample Input XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Distribution>
<ManifestHeader>
<Assets>
<Asset>
<ID>23341528</ID>
<CreateDate>2008-01-14T17:02:01Z</CreateDate>
<MetaDatas>
<MetaData Name="psa.orig.source.showTitle">Green Home 2008</MetaData>
<MetaData Name="displayRunTime">00:01</MetaData>
<MetaData Name="Series Title">Desperate Landscapes</MetaData>
</MetaDatas>
</Asset>
<Asset>
<ID>23341529</ID>
<CreateDate>2010-08-23T15:44:58Z</CreateDate>
<MetaDatas>
<MetaData Name="psa.orig.source.showTitle">Urban Oasis 2010</MetaData>
<MetaData Name="displayRunTime">00:02</MetaData>
<MetaData Name="Series Title">Toy Hunter</MetaData>
</MetaDatas>
</Asset>
<Asset>
<ID>23377202</ID>
<CreateDate>2007-05-18T07:40:25Z</CreateDate>
<MetaDatas>
<MetaData Name="webSeries"/>
<MetaData Name="psa.orig.source.showTitle">Cool Tools</MetaData>
<MetaData Name="displayRunTime">00:20</MetaData>
<MetaData Name="Series Title">Desperate Landscapes</MetaData>
</MetaDatas>
</Asset>
</Assets>
</ManifestHeader>
</Distribution>
XLST:
<xsl:key name="keySeriesName" match="MetaData[#Name='Series Title']" use="text()" />
<xsl:for-each select="MetaData[#Name='Series Title'][generate-id() =
generate-id(key('keySeriesName', text())[1])]">
also tried:
<xsl:for-each select="MetaData[#Name='Series Title'][count(. | key('keySeriesName',text())[1]) = 1]">
anyhelp would be appreciated
Thanks in advance
Since the <MetaData> elements are children of <MetaData> and you are trying to search across the entire collection of them within the document, you are going to need to adjust your XPath to ensure that you are addressing all of them:
/Distribution/ManifestHeader/Assets/Asset/MetaDatas/MetaData
[#Name='Series Title'][generate-id() =
generate-id(key('keySeriesName', text())[1])]
or you could use the shorter, but less efficient:
//MetaData[#Name='Series Title'][generate-id() =
generate-id(key('keySeriesName', text())[1])]