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
Related
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.
I'm trying to simulate the copy-of function in XSLT where I want everything within a node outputted in the response.
Using this template
<#ftl ns_prefixes={"D": "http://milyn.codehaus.org/Smooks"} output_format="XML">
${Order.orderitem.##markup}
Facing 2 issues here
The output i get transformed the <, > as well of the XML tags. I do need XML formatting to escape invalid characters like & etc.
How can i remove the namescapes that appears in every node
My response is
<orderitem xmlns="http://milyn.codehaus.org/Smooks"><position>1</position><quantity>1</quantity><productid>364</productid><title>The 40YearOld</title><price>29.98</price></orderitem><orderitem xmlns="http://milyn.codehaus.org/Smooks"><position>2</position><quantity>1</quantity><productid>299</productid><title>Pulp Fiction</title><price>29.99</price></orderitem>
Input being
<Order xmlns="http://milyn.codehaus.org/Smooks" xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance">
<header>
<orderid>1</orderid>
<statuscode>0</statuscode>
<netamount>59.97</netamount>
<totalamount>64.92</totalamount>
<tax>4.95</tax>
<date>Wed Nov 15 13:45:28 EST 2006</date>
</header>
<customerdetails>
<username>user1</username>
<name>
<firstname>Harry</firstname>
<lastname>Fletcher</lastname>
</name>
<state>South Dakota</state>
</customerdetails>
<orderitem>
<position>1</position>
<quantity>1</quantity>
<productid>364</productid>
<title>The 40YearOld</title>
<price>29.98</price>
</orderitem>
<orderitem>
<position>2</position>
<quantity>1</quantity>
<productid>299</productid>
<title>Pulp Fiction</title>
<price>29.99</price>
</orderitem>
To prevent auto-escaping: ${Order.orderitem.##markup?no_esc}. (Unfortunately XML wrapping way predates auto-escaping, so it has remained like so...)
Prevent repeated xmlns-es... you can't. The problem is that the orderitem-s has no common ancestor as far as ##markup can know, where a common xmlns could solve this, so it does the safest thing.
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".
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?
I have requirement on to find the duplicate elements in the input xml and sum the quantity with single record as output.
Input xml is:
<Input>
<A1>
<NAME>A</NAME>
<QTY>1</QTY>
</A1>
<A1>
<NAME>A</NAME>
<QTY>2</QTY>
</A1>
<A2>
<NAME>B</NAME>
<QTY>3</QTY>
</A2>
<A1>
<NAME>A</NAME>
<QTY>5</QTY>
</A1>
<A2>
<NAME>b</NAME>
<QTY>8</QTY>
</A2>
</Input>
output should be as below:
<Input>
<A1>
<NAME>A</NAME>
<QTY>8</QTY>
</A1>
<A2>
<NAME>B</NAME>
<QTY>11</QTY>
</A2>
</Input>
If you want to sum several nodes of type number you can use the XPath sum() function. This adds all your QTY nodes:
sum(//QTY)
If you just want to add the nodes that are below A1 you can use:
sum(/Input/A1/QTY)
or
sum(//A1/QTY)
which will have the same result considering the source you provided.
You can select the first A1 with the same name using
//A1[1]
So, to obtain the result you want you could match A1[1] in a template and call sum(//A1/QTY) or sum(/Input/A1/QTY) inside it to obtain the sum. Then you repeat the process with A2.
You can achieve this with two recursive templates:
The sum expression here obtains the value of the node * which may be A1 or A2. The XPath expression compares its name name(current()) with the name() of each child of Input (/Input/*), which will match either A1 or A2, adding the amount in the QTY of each node.