XSLT namespace issue in BizTalk mapping - xslt

Below is my source schema
<ns0:xyz xmlns:ns0="http://abc/xyz">
<main>
<zzz/></zzz>
<yyy/></yyy>
</main>
<Lines>
<Line>
<LineNum></LineNum>
<Linerate></Linerate>
</Line>
</Lines>
and Below is my input file:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xyz xmlns="http://abc/xyz">
<main>
<zzz>12</zzz>
<yyy>11</yyy>
</main>
<Lines>
<Line>
<LineNum>1</LineNum>
<Linerate>0.5</Linerate>
</Line>
<LineNum>3</LineNum>
<Linerate>0.2</Linerate>
</Line>
<Line>
<LineNum>5</LineNum>
<Linerate>0.5</Linerate>
</Line>
</Lines>
I need to compare the line records and check if the Linerate element has similar value , then irrespective of the number of records in the source file , my destination file should have only 1 record with that particular Line rate value.
And for every distinct Linerate value , there should be respective record for it.
The Linerate value should be assigned to percent node in the destination
The Cumulative sum value of the LineNum value should be assigned to Amount in destination(if Linerate value is same across the records in the source)
Linerate * cumulative sum of LineNum should be assinged to AdditionalAmount in destination
Below is the expected output file
<LineSummary>
<Percent>0.5</Percent>
<Amount>6</Amount>
<AdditionalAmount>3</AdditionalAmount>
</LineSummary>
<LineSummary>
<Percent>0.2</Percent>
<Amount>3</Amount>
<AdditionalAmount>0.6</AdditionalAmount>
</LineSummary>
Below is the XSLT code used in my BizTalk Map.
<xsl:variable name="unique-LineRate" select="//Lines/Line[not(Linerate=preceding-sibling::Line/Linerate)]/Linerate" />
<xsl:for-each select="$unique-LineRate">
<LineSummary>
<xsl:variable name="LineSum" select="sum(//Lines/Line[Linerate=current()]/LineNum)" />
<Percent><xsl:value-of select="current()"/></Percent>
<Amount><xsl:value-of select="$LineSum" /></Amount>
<AdditionalAmount><xsl:value-of select="current() * $LineSum"/></AdditionalAmount>
</LineSummary>
</xsl:for-each>
If my source schema "ElementFormDefault" value is kept as unqualified or default, the I get an error as : Input validation error: The element 'xyz' in namespace 'http://abc/xyz' has invalid child element 'main' in namespace 'http://abc/xyz'. List of possible elements expected: 'main'.
If my source schema "ElementFormDefault" is kept as Qualified,then the XSLT doesn't work. I am sure it has to do something with the namespace issue or element tagging But i am not sure exactly where i need to make the change.
Do I need to prefix with the XPath or the namespace to all the elements in the XSLT?

Related

In XSLT nested foreach loop always return first element

XLST receiving the date from apache-camel in below formate.
data format
<list>
<linked-hash-map>
<entry key="NAME">test1</entry>
</linked-hash-map>
<linked-hash-map>
<entry key="NAME">test2</entry>
</linked-hash-map>
</list>
My XSLT:
<xsl:stylesheet>
<xsl:template match="*">
<xsl:for-each select="//*[local-name()='linked-hash-map']">
<tag1>
<xsl:value-of select="string(//*[local-name()='entry'][#key='NAME'])"/>
</tag1t>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
OUTPUT always returns the first element.
<tag1>test1<tag1>
<tag1>test1<tag1>
What is wrong in above xslt and help generate xml with all elements.
Because path expressions starting with "//" select from the root of the document tree, you are selecting the same nodes every time in your xsl:value-of; and in XSLT 1.0, if you select multiple nodes, only the first one gets displayed.
Methinks you're using "//" because you've seen it in example code and don't actually understand what it means...
Within xsl:for-each, you normally want a relative path that selects from the node currently being processed by the for-each.
You've also probably picked up this *[local-name()='linked-hash-map'] habit from other people's code. With no namespaces involved, you can safely replace it with linked-hash-map.

XML to XML XSLT transformation. MSXML in VBScript

I have a fairly nested XML file that I'd like to transform with an XSL template to something a little simpler to make bulk loading the data into SQL more efficient. I wanted to do it in C++ (Codeblocks with gcc) but I'm having a bit of trouble just being able to load the document with any of the libraries I've come across, including MSXML. If anyone has any experience using MSXML in Codeblocks with gcc let me know!
I have a stylesheet that transforms the XML in Excel VBA with a DOMDocument but I don't want to depend on Excel. I figured the next best thing would be a VBScript.
The data are one or two text values that are held in <DATAVALUE> nodes, descendants of 100 <LOCATION> nodes. The first child of each <LOCATION> node, called <LOCATIONNAME>, holds a unique name for each <LOCATION> node (i.e; NAME1-NAME100). The third and fourth children of the <LOCATION> node (if there is a fourth child) are <DATA> nodes, each holding a <DATAVALUE> node. The file can have upwards of 1 million <SAMPLE> nodes. Here is the XML:
<?xml version="1.0" encoding="utf-8"?>
<MYImportFile xmlns="urn:ohLookHEREaNamespacedeclaration">
<HEADERVERSION>1.10</HEADERVERSION>
<MESSAGE>Import</MESSAGE>
<MYBED>QUEEN</MYBED>
<SOURCE>SPRING </SOURCE>
<USERID>MMOUSE</USERID>
<DATETIME>2019-11-25T12:31:00</DATETIME>
<SAMPLE TYPE="No" APPLE="false">
<SAMPLEID>0000565</SAMPLEID>
<SAMPLECATEGORY>CLASS5</SAMPLECATEGORY>
<LOCATION APPLE="false">
<LOCATIONNAME>NAME1</LOCATIONNAME>
<READBY>MMOUSE</READBY>
<TIME>12:31:00</TIME>
<DATA>
<DATAVALUE>aaaa</DATAVALUE>
</DATA>
<DATA>
<DATAVALUE>bbbb</DATAVALUE>
</DATA>
</LOCATION>
'''''''''''''''''there are 100 LOCATION entries''''''''''''''''''''''''
<LOCATION APPLE="false">
<LOCATIONNAME>NAME100</LOCATIONNAME>
<READBY>MMOUSE</READBY>
<TIME>12:31:00</TIME>
<DATA>
<DATAVALUE>zzzz</DATAVALUE>
</DATA>
</LOCATION>
</SAMPLE>
'''''''''''''''''repeat for however many SAMPLES there are''''''''''''''''''''''
</MYImportFile>
I want to point something out so it's a little more clear what's going on. In the transformed xml document, one of the things I need to account for is when there is only one <DATA> node in a <LOCATION>. This is done by copying the first <DATAVALUE> node into a second <DATAVALUE> node in the new document. For example, the <DATAVALUE>, "zzzz" that appears twice in the transformed sheet only appears in the initial XML once. Here is what I want the transformed XML to look like:
<?xml version="1.0" encoding="UTF-8"?>
<MYImportFile>
<SAMPLE>
<SAMPLEID>0000565</SAMPLEID>
<NAME1_1>aaaa</NAME1_1>
<NAME1_2>bbbb</NAME1_2>
<NAME2_1>cccc</NAME2_1>
<NAME2_2>dddd</NAME2_2>
'''''''''''''''''there are 100 LOCATION entries transformed to NAME1-NAME100''''''''''''''''''''''''
<NAME100_1>zzzz</NAME100_1>
<NAME100_2>zzzz</NAME100_2>
</SAMPLE>
'''''''''''''''''repeat for however many SAMPLES there are''''''''''''''''''''''
</MYImportFile>
My StyleSheet (that works with VBA code):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:b="urn:ohLookHEREaNamespacedeclaration" exclude-result-prefixes="b">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/b:MYImportFile">
<MYImportFile>
<xsl:for-each select="b:SAMPLE">
<SAMPLE>
<SAMPLEID>
<xsl:value-of select="b:SAMPLEID"/>
</SAMPLEID>
<NAME1_1>
<xsl:value-of select="b:LOCATION/b:LOCATIONNAME[text() = 'NAME1']/../b:DATA[1]/b:DATAVALUE"/>
</NAME1_1>
<xsl:choose>
<xsl:when test="b:LOCATION/b:LOCATIONNAME[text() = 'NAME1']/../b:DATA[2]/b:DATAVALUE">
<NAME1_2>
<xsl:value-of select="b:LOCATION/b:LOCATIONNAME[text() = 'NAME1']/../b:DATA[2]/b:DATAVALUE"/>
</NAME1_2>
</xsl:when>
<xsl:otherwise>
<NAME1_2>
<xsl:value-of select="b:LOCATION/b:LOCATIONNAME[text() = 'NAME1']/../b:DATA[1]/b:DATAVALUE"/>
</NAME1_2>
</xsl:otherwise>
</xsl:choose>
'''''''''''''''''''there are 100 NAME entires to recieve the 100 locations
</SAMPLE>
</xsl:for-each>
</MYImportFile>
</xsl:template>
</xsl:stylesheet>
My Script:
Option Explicit
Const strInputFile = "C:\Path\fileName.xml"
Const strTemplateFile = "C:\Path\convFileName.xsl"
Const strOutputFile = "C:\Path\newFileName.xml"
Dim objXMLDoc : Set objXMLDoc = WScript.CreateObject("Msxml2.DOMDocument")
objXMLDoc.async = False
objXMLDoc.loadXML(strInputFile)
objXMLDoc.SetProperty "SelectionNamespaces", "xmlns='urn:myNamespace'"
Dim objXSLDoc : Set objXSLDoc = WScript.CreateObject("Msxml2.DOMDocument")
objXSLDoc.async = False
objXSLDoc.loadXML(strTemplateFile)
Dim objNewXMLDoc : Set objNewXMLDoc = WScript.CreateObject("Msxml2.DOMDocument")
objXMLDoc.transformNodeToObject objXSLDoc, objNewXMLDoc
objNewXMLDoc.save strOutputFile
The error:
Line: 19
Char: 1
Error: The stylesheet does not contain a document element. The
stylesheet may be empty, or it may not be a well-formed XML document.
Code: 80004005
Source: msxml3.dll
I'm guessing either my script isn't quite right or there's a setting I'm missing, causing mismatching objects and libraries, because my VBA macro transforms the xml with that stylesheet. Anyone have any ideas? Suggestions to make this thing run?
As far as I remember loadXML takes a string with the XML. If you have a file or URL to parse use the load method.

XSLT element count

I want to count no of elements under a node , In below XML I has 2 report entries , In first entry I has 4 elements and in 2nd entry I has only 3 address is missing.
I want to count the total elements under each report_entry
count is 4 for first report entry and count is 3 for 2nd report entry
Can some body suggest is it possible ? any sample code ?
example xml:
<?xml version="1.0" encoding="UTF-8"?>
<report_data>
<report_entry>
<id>12345</id>
<fname>Venkata</fname>
<lname>Penumatsa</lname>
<address>Hyderabad</address>
</report_entry>
<report_entry>
<id>123453</id>
<fname>stephen</fname>
<lname>florida</lname>
</report_entry>
</report_data>
-- Venkata
Use
<xsl:template match="report_entry">
<xsl:value-of select="count(*)"/>
</xsl:template>
if you "want to count the total elements under each report_entry".

Stuctured XML to Map or Flat XML

I need to insert the line items on my XML to a Map or a flat XML in mulesoft. Iam planning to use XSLT but Im having only single values instead of multiple Line Items. Im not sure how the for each function works for this. any help would be appreciated.
Input
<?xml version="1.0" encoding="utf-8"?><XmlInterchange xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1" xmlns="http://www.edi.com.au/EnterpriseService/">
<InterchangeInfo>
<Date>2016-02-29T05:56:10.272+05:00</Date>
<XmlType>LightWeight</XmlType>
<Source></Source>
<Target></Target>
</InterchangeInfo>
<Payload>
<WhsDockets>
<WhsDocket>
<Identifier>
<Reference>2370519</Reference>
</Identifier>
<DocketDetail>
<WarehouseCode>ROC</WarehouseCode>
<CustomerReference>3340527</CustomerReference>
<Units>41</Units>
<Packages>0</Packages>
<Pallets>0</Pallets>
<Weight DimensionType="KG">720</Weight>
<Cubic DimensionType="M3">5.922</Cubic>
<TransportInsurance>0.0000</TransportInsurance>
<ShipperCODAmount>0.0000</ShipperCODAmount>
<CustomerOrderDetail>
<OrderType>ORD</OrderType>
<DateRequired>2015-09-02T00:00:00</DateRequired>
<Consignee AddressType="CEA">
<AddressLine1>Cnr Maroochydore and BroadmeadowRds</AddressLine1>
<CityOrSuburb>MAROOCHYDORE</CityOrSuburb>
<StateOrProvince>QLD</StateOrProvince>
<PostCode>4558</PostCode>
<CompanyName>Bunnings Maroochydore OLD Warehouse</CompanyName>
<CountryCode>AU</CountryCode>
<ContactName>The Import Manager</ContactName>
</Consignee>
</CustomerOrderDetail>
<CustomAttributes />
</DocketDetail>
<DocketLines>
<DocketLine>
<Product>E4342</Product>
<Description>R 3 5/3 6 175mm x 430mm x 1160mm</Description>
<QuantityFromClientOrder>5</QuantityFromClientOrder>
<QuantityActuallyOrdered>5</QuantityActuallyOrdered>
<ProductUQ>MST</ProductUQ>
<LineAttributes />
<LineNumber>1</LineNumber>
<Confirmation>
<Lines>
<Line>
<Quantity>25</Quantity>
<QuantityUQ>PAC</QuantityUQ>
</Line>
</Lines>
<Quantity>25</Quantity>
</Confirmation>
</DocketLine>
<DocketLine>
<Product>E2281</Product>
<Description>R 3 5 175mm x 580mm x 1160mm</Description>
<QuantityFromClientOrder>4</QuantityFromClientOrder>
<QuantityActuallyOrdered>4</QuantityActuallyOrdered>
<ProductUQ>MST</ProductUQ>
<LineAttributes />
<LineNumber>2</LineNumber>
<Confirmation>
<Lines>
<Line>
<Quantity>16</Quantity>
<QuantityUQ>PAC</QuantityUQ>
</Line>
</Lines>
<Quantity>16</Quantity>
</Confirmation>
</DocketLine>
</DocketLines>
</WhsDocket>
</WhsDockets>
</Payload></XmlInterchange>
I need to flatten the XML but use the Litem Item details together with the Reference Number per each Item.
Output
<?xml version="1.0" encoding="utf-8"?><Items>
<LineItem>
<Date/>
<Order>2370519</Order>
<Client>Bunnings Maroochydore OLD Warehouse</Client>
<Product>E2281</Product>
<Description>R 3 5 175mm x 580mm x 1160mm</Description>
<Quantity>4</Quantity>
<UOM>MST</UOM>
<Warebouse>ROC</Warebouse>
<Carrier>Deluxe</Carrier>
</LineItem>
</Items>
Have you looked at DataWeave to transform it from current xml to new xml?
https://docs.mulesoft.com/mule-user-guide/v/3.7/dataweave-examples#xml-basic

XSLT Get First Element Node

<SMRCRLT_XML>
<AREA>
<DETAILS>
<DETAIL_REQUIREMENT>
<RULE_REQUIREMENT>
<DETAIL_REQUIREMENT>
<COURSE_ROWSET>
<COURSE_SET>
<COURSE_AREA>TESTSELECT</COURSE_AREA>
<COURSE_KEY_RULE>1200</COURSE_KEY_RULE>
<COURSE_SET>A</COURSE_SET>
<COURSE_SUBSET>1</COURSE_SUBSET>
<COURSE_SUBJ_CODE>CHEM</COURSE_SUBJ_CODE>
<COURSE_CRSE_NUMB_LOW>345A</COURSE_CRSE_NUMB_LOW>
</COURSE_SET>
</COURSE_ROWSET>
</DETAIL_REQUIREMENT>
<DETAIL_REQUIREMENT>
<COURSE_ROWSET>
<COURSE_SET>
<COURSE_KEY_RULE>1200</COURSE_KEY_RULE>
<COURSE_SET>A</COURSE_SET>
<COURSE_SUBSET>2</COURSE_SUBSET>
<COURSE_SUBJ_CODE>CHEM</COURSE_SUBJ_CODE>
<COURSE_CRSE_NUMB_LOW>476A</COURSE_CRSE_NUMB_LOW>
</COURSE_SET>
</COURSE_ROWSET>
</DETAIL_REQUIREMENT>
<DETAIL_REQUIREMENT>
<COURSE_ROWSET>
<COURSE_SET>
<COURSE_AREA>TESTSELECT</COURSE_AREA>
<COURSE_KEY_RULE>1200</COURSE_KEY_RULE>
<COURSE_SET>A</COURSE_SET>
<COURSE_SUBSET>3</COURSE_SUBSET>
<COURSE_SUBJ_CODE>PHIL</COURSE_SUBJ_CODE>
<COURSE_CRSE_NUMB_LOW>432</COURSE_CRSE_NUMB_LOW>
</COURSE_SET>
</COURSE_ROWSET>
</DETAIL_REQUIREMENT>
<DETAIL_REQUIREMENT>
<COURSE_ROWSET>
<COURSE_SET>
<COURSE_AREA>TESTSELECT</COURSE_AREA>
<COURSE_KEY_RULE>1200</COURSE_KEY_RULE>
<COURSE_SET>B</COURSE_SET>
<COURSE_SUBSET>4</COURSE_SUBSET>
<COURSE_SUBJ_CODE>PHIL</COURSE_SUBJ_CODE>
<COURSE_SUBJ_DESC>Philosophy</COURSE_SUBJ_DESC>
<COURSE_CRSE_NUMB_LOW>433</COURSE_CRSE_NUMB_LOW>
</COURSE_SET>
</COURSE_ROWSET>
</DETAIL_REQUIREMENT>
<DETAIL_REQUIREMENT>
<COURSE_ROWSET>
<COURSE_SET>
<COURSE_AREA>TESTSELECT</COURSE_AREA>
<COURSE_KEY_RULE>1200</COURSE_KEY_RULE>
<COURSE_SET>B</COURSE_SET>
<COURSE_SUBSET>5</COURSE_SUBSET>
<COURSE_SUBJ_CODE>ZOOL</COURSE_SUBJ_CODE>
<COURSE_CRSE_NUMB_LOW>321</COURSE_CRSE_NUMB_LOW>
</COURSE_SET>
</COURSE_ROWSET>
</DETAIL_REQUIREMENT>
<DETAIL_REQUIREMENT>
<COURSE_ROWSET>
<COURSE_SET>
<COURSE_AREA>TESTSELECT</COURSE_AREA>
<COURSE_KEY_RULE>1200</COURSE_KEY_RULE>
<COURSE_SET>B</COURSE_SET>
<COURSE_SUBSET>6</COURSE_SUBSET>
<COURSE_SUBJ_CODE>BIOC</COURSE_SUBJ_CODE>
<COURSE_CRSE_NUMB_LOW>456</COURSE_CRSE_NUMB_LOW>
</COURSE_SET>
</COURSE_ROWSET>
</DETAIL_REQUIREMENT>
</RULE_REQUIREMENT>
</DETAIL_REQUIREMENT>
</DETAILS>
</AREA>
</SMRCRLT_XML>
I am trying to get the first element from the XML for each COURSE_SET, but it returns all the values. Can someone please help. This is my template that I applied:
<xsl:apply-templates select="//SMRCRLT_XML/AREA/DETAILS/DETAIL_REQUIREMENT/RULE_REQUIREMENT/DETAIL_REQUIREMENT/COURSE_ROWSET/COURSE_SET[COURSE_AREA='TESTSELECT' and COURSE_KEY_RULE='1200'][1]"/>
The results I am getting are:
CHEM345A
PHIL432
PHIL433
ZOOL321
BIOC456
The result I am looking for is CHEM 345A and then PHIL433
You have several problems here.
First, the [1] in your XPath expression is filtering the XPath value by requiring that the COURSE_SET elements selected be the first child of their parent. Without that [1], your XPath expression reads:
//SMRCRLT_XML
/AREA
/DETAILS
/DETAIL_REQUIREMENT
/RULE_REQUIREMENT
/DETAIL_REQUIREMENT
/COURSE_ROWSET
/COURSE_SET
[COURSE_AREA='TESTSELECT' and COURSE_KEY_RULE='1200']
But every COURSE_SET that matches that path expression is the first child of its parent. (The only COURSE_SET elements which are not first children are children of COURSE_SET, not children of COURSE_ROWSET.)
The second problem is that it appears, judging by your question and your attempt at formulating the XPath expression you want, that you would like the courses to be grouped somehow (at first I thought you might want them grouped by department but now I expect you want them grouped by the value of the nested COURSE_SET element, which in your example has values A or B), so that by selecting the first COURSE_SET in some suitable context you can get the first course listed for each group. But the XML you show doesn't in fact group the courses by department or by course set; it provides a flat list of courses with no groupings at all. There are no elements here for which CHEM 345A and PHIL 433 are the first courses.
If your design calls for the courses to be grouped by department or course set, then your data source is not providing the data you want, and you will want to fix it.
If on the other hand you're stuck with this XML and want to use XPath to try to provide the structure that your data source is not capable of providing, then you don't want "the first element for each COURSE_SET", you want "each COURSE_SET which is in a department (or a COURSE_SET) different from the immediately preceding COURSE_SET". And your XPath expression can be something like
//COURSE_ROWSET/COURSE_SET
[not(COURSE_SET eq preceding::COURSE_SET[1])]
Your third problem is that your XML seems to be too fond of using the same name for different constructs (one set of COURSE_SET elements each of which contains a description of a course, with department and course number and so on, and a second set of COURSE_SET elements which contain the strings 'A' and 'B', two sets of DETAIL_REQUIREMENT with different content, and so on. It's confusing for people not familiar with the data, and it will make every single discussion of detail an opportunity for miscommunication and error.
The efficient way to handle a task like this in XSLT 1.0 is to use Muenchian grouping, like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="kSet" match="COURSE_ROWSET/COURSE_SET" use="COURSE_SET" />
<xsl:template match="/">
<root>
<xsl:apply-templates
select="//COURSE_ROWSET/COURSE_SET[generate-id() =
generate-id(key('kSet', COURSE_SET)[1])]" />
</root>
</xsl:template>
<xsl:template match="COURSE_ROWSET/COURSE_SET">
<item>
<xsl:value-of select="concat(COURSE_SUBJ_CODE, COURSE_CRSE_NUMB_LOW)"/>
</item>
</xsl:template>
</xsl:stylesheet>
When this XSLT is applied to your sample input, the result is:
<root>
<item>CHEM345A</item>
<item>PHIL433</item>
</root>