XSLT: Find the way to transform to XML - xslt

I have XML in the following format:
<?xml version="1.0"?>
<MES_VEHICLE_STATUS_DESCRIPTOR xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="MOM.Production" PRODUCTIONORDER="000001995795" >
<POSECTION LOCATION="" />
<ORDERSECTION SALESORDER="" />
<MATERIALCONSUMPTIONSECTION>
<MATERIAL_CONSUMPTION DESC="car" QTY="1.000" WORKPLACE="3001_GAT101LH">BIN1000004</MATERIAL_CONSUMPTION>
<MATERIAL_CONSUMPTION DESC="PLAIN_WASHER" QTY="1.000" WORKPLACE="3001_GAT101LH">WRE20000018</MATERIAL_CONSUMPTION>
<MATERIAL_CONSUMPTION DESC="car" QTY="1.000" WORKPLACE="3001_GAT101LH">BIN1000003</MATERIAL_CONSUMPTION>
</MATERIALCONSUMPTIONSECTION>
</MES_VEHICLE_STATUS_DESCRIPTOR>
and output should look like that:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:mat="http://mes.com/ScrapManagement">
<soapenv:Header/>
<soapenv:Body>
<mat:DT_SCRAPMANAGEMENT_MES_REQ>
<DATA>
<PlantCode>3002</PlantCode>
<SAPOrder>000001995795</SAPOrder>
<Quantity>1.000</Quantity>
<PartNo>BIN1000004</PartNo>
<WorkPlace>3001_GAT101LH</WorkPlace>
</DATA>
<DATA>
<PlantCode>3002</PlantCode>
<SAPOrder>000001995795</SAPOrder>
<Quantity>1.000</Quantity>
<PartNo>WRE20000018</PartNo>
<WorkPlace>3001_GAT101LH</WorkPlace>
</DATA>
<DATA>
<PlantCode>3002</PlantCode>
<SAPOrder>000001995795</SAPOrder>
<Quantity>1.000</Quantity>
<PartNo>BIN1000003</PartNo>
<WorkPlace>3001_GAT101LH</WorkPlace>
</DATA>
</mat:DT_SCRAPMANAGEMENT_MES_REQ>
</soapenv:Body>
</soapenv:Envelope>
My XSLT to transform
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0" />
<xsl:template match="MES_VEHICLE_STATUS_DESCRIPTOR">
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:mat="http://mes.com/ScrapManagement">
<soapenv:Header />
<soapenv:Body>
<xsl:element name="mat:DT_SCRAPMANAGEMENT_MES_REQ">
<xsl:variable name="po" select="#PRODUCTIONORDER" />
<xsl:for-each select="MATERIALCONSUMPTIONSECTION/MATERIAL_CONSUMPTION">
<DATA>
<PlantCode>
<xsl:text>3002</xsl:text>
</PlantCode>
<SAPOrder>
<xsl:value-of select="$po" />
</SAPOrder>
<Quantity>
<xsl:value-of select="#QTY" />
</Quantity>
<PartNo>
<xsl:value-of select="." />
</PartNo>
<WorkPlace>
<xsl:value-of select="#WORKPLACE" />
</WorkPlace>
</DATA>
</xsl:for-each>
</xsl:element>
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
</xsl:stylesheet>
If i delete xmlns="MOM.Production" in XML file, the transformation is OK. But if have it in xml file, cannot transform. I use website: https://www.online-toolz.com/tools/xslt-transformation.php and show the error: Error:DOMDocument::loadXML(): xmlns: URI MOM.Production is not absolute in Entity, line: 2.
I don't understand what is reason.
I don't understand what is reason

The code you had in mind is probably more or less this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0" />
<xsl:template match="MES_VEHICLE_STATUS_DESCRIPTOR">
<xsl:for-each select="MATERIALCONSUMPTIONSECTION/MATERIAL_CONSUMPTION">
<xsl:element name="DATA">
<xsl:element name="PRODUCTIONORDER">
<xsl:value-of select="../../#PRODUCTIONORDER" />
</xsl:element>
<xsl:element name="QTY">
<xsl:value-of select="#QTY" />
</xsl:element>
<xsl:element name="MATERIAL">
<xsl:value-of select="text()" />
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I think you have to understand the difference between relative and absolute paths in the XPATH expressions your are using to select the correct nodes.
When you are inside a template matching the node MES_VEHICLE_STATUS_DESCRIPTOR, you don't use absolute paths like /MATERIALCONSUMPTIONSECTION/MATERIAL_CONSUMPTION (which doesn't exist in this case), but paths relative to this node, as MATERIALCONSUMPTIONSECTION/MATERIAL_CONSUMPTION.
When you are inside a <xsl:for-each select="MATERIALCONSUMPTIONSECTION/MATERIAL_CONSUMPTION"> loop, you have to select the nodes with relative paths like text(), ../../#PRODUCTIONORDER or #QTY. Notice the # telling the QTY and PRODUCTIONORDER are attribute type nodes.
Finally, your current output is no well formed XML since it lacks a root element.
Personnaly, I would replace the <xsl:element> tag by its litteral value and avoid the computation of a constant field accross a loop:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0" />
<xsl:template match="MES_VEHICLE_STATUS_DESCRIPTOR">
<xsl:variable name="po" select="#PRODUCTIONORDER" />
<xsl:for-each select="MATERIALCONSUMPTIONSECTION/MATERIAL_CONSUMPTION">
<DATA>
<PRODUCTIONORDER><xsl:value-of select="$po" /></PRODUCTIONORDER>
<QTY><xsl:value-of select="#QTY" /></QTY>
<MATERIAL><xsl:value-of select="." /></MATERIAL>
</DATA>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Related

How to fetch value of n element without using variable?

I want to fetch values from elements without storing values in a variable. My request is long so I'm making it short here. It contains multiple tags. I tried with my XSLT but couldn't achieve the expected output. Due to multiple tags I'm unable to get into the correct path. How can I achieve below output without using variables in my XSLT ?
Below is my xml request:
<documents>
<document>
<doc1>1234</doc1>
<doc2>4578</doc2>
<locations>
<location>
<companies>
<company>
<type>E-123</type>
<empid>E-457</empid>
<pays>
<pay>
<pay1>2L</pay1>
<pay2>4L</pay2>
</pay>
</pays>
<Pays>
<pay>
<due>2L</due>
<month>30k</month>
</pay>
</pays>
</company>
</companies>
</location>
</locations>
</document>
</documents>
Here is my expected output:
<empdetails>
<details>
<emptype>E-123</emptype>
<ID>E-457</ID>
<Payment>2L</Payment>
<Payment2>4L</Payment2>
<dueperyr>50k</dueperyr>
<duemonth>30k</duemonth>
</details>
</empdetails>
XSLT which I tried:
<xsl:stylesheet extension-element-prefixes="dp apim" version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="documents">
<soapenv:Envelope>
<soapenv:Header/>
<soapenv:Body>
<xsl:apply-templates select="/documents/document" />
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
<xsl:template match="companies">
<empdetails>
<details>
<emptype>
<xsl:value-of select="company/type"/>
</emptype>
<ID>
<xsl:value-of select="company/empid"/>
</ID>
</xsl:template>
<xsl:template match="pays">
<xsl:for-each select="pays/pay">
<Payment>
<xsl:value-of select="pay/pay1"/>
</Payment>
<Payment2>
<xsl:value-of select="pay/pay2"/>
</Payment2>
</xsl:template>
<dueperyr>
<xsl:value-of select="pay/due"/>
</dueperyr>
<duemonth>
<xsl:value-of select="pay/month"/>
</duemonth>
</xsl:for-each>
</details>
</empdetails>
</xsl:stylesheet>
You can try this
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="documents">
<soapenv:Envelope>
<soapenv:Header/>
<soapenv:Body>
<xsl:apply-templates select="/documents/document" />
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
<xsl:template match="companies">
<empdetails>
<details>
<emptype>
<xsl:value-of select="company/type"/>
</emptype>
<ID>
<xsl:value-of select="company/empid"/>
</ID>
<xsl:for-each select="company/pays">
<xsl:apply-templates select="pay"/>
</xsl:for-each>
</details>
</empdetails>
</xsl:template>
<xsl:template match="pay1">
<Payment>
<xsl:apply-templates/>
</Payment>
</xsl:template>
<xsl:template match="pay2">
<Payment2>
<xsl:apply-templates/>
</Payment2>
</xsl:template>
<xsl:template match="due">
<dueperyr>
<xsl:apply-templates/>
</dueperyr>
</xsl:template>
<xsl:template match="month">
<duemonth>
<xsl:apply-templates/>
</duemonth>
</xsl:template>
</xsl:stylesheet>
link: https://xsltfiddle.liberty-development.net/eieFA1G

XSL attirbutes with the same name

I am writing an xslt transformation for below XLS code:
<?xml version="1.0"?>
<OTA_HotelPmsInfoNotif xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" EchoToken="PMS" Version="0.101" PrimaryLangID="en" ClosureDate="2020-10-29" RetransmissionIndicator="true" SequenceNmbr="2" TimeStamp="2020-10-29T23:51:00Z">
<POS>
<Source>
<RequestorID Type="81" ID="POF" ID_Context="parol"/>
</Source>
<Source ISOCountry="CZ" ISOCurrency="CZE">
<RequestorID Type="10" ID="H1111" ID_Context="star">
<CompanyName>Pharmacy Prague</CompanyName>
</RequestorID>
</Source>
</POS>
</OTA_HotelPmsInfoNotif>
I would like to pull out of this XML attribute named ID but as you can see there are two ID attributes, ID="POF" and ID = "H1111". As for now i have what follows:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:template match="/">
<xsl:text>RID</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="OTA_HotelPmsInfoNotif/POS/Source/RequestorID">
<xsl:text>"</xsl:text>
<xsl:if test ="#ID='H1111'">
<xsl:value-of select="#ID"/>
</xsl:if>
<xsl:text>";"</xsl:text>
<xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
But this code enters one empty line for ID="POF". How to extract only one atrribute ID="H1111"?
If you know the value of the Type attribute, you can do simply:
<xsl:value-of select="OTA_HotelPmsInfoNotif/POS/Source/RequestorID[#Type='10']/#ID"/>
Demo: https://xsltfiddle.liberty-development.net/aiyndY

XSLT transformation with multiple nodes

I have multiple occurence nodes which need to be generated at output using XSLT transformation. Could you please help me on this.
Following XSLT code only generate one node occurrence only. Could you please help me with below XSLT code how to generate multiple nodes elements in Input XML
Input XML
<?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Body>
<ns1:getGenResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
<ns1:getGenReturn xsi:type="soapenc:Array" soapenc:arrayType="xsd:anyType[2]" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
</ns1:getGenReturn>
</ns1:getGenResponse>
<multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns2:Gen" xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>
<name xsi:type="xsd:string">ULM</name>
<mail xsi:type="xsd:string">ulm#gmail.com</mail>
</multiRef>
<multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns3:Gen" " xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<name xsi:type="xsd:string">ABC</name>
<mail xsi:type="xsd:string">abc#gmail.com</mail>
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
XSLT Code used for this transformation
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" x
xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:if test="//soap:Body/multiRef">
<xsl:element name="getGenResponse">
<xsl:element name="getGenReturn">
<xsl:element name="name"><xsl:value-of select="//name"/></xsl:element>
<xsl:element name="mail"><xsl:value-of select="//mail"/></xsl:element>
</xsl:element>
</xsl:element>
</xsl:if>
</xsl:template>
<!-- 'Copy ' node -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output from above XSLT
<?xml version="1.0" encoding="UTF-8"?>
<getGenResponse>
<getGenReturn>
<name> ULM </name>
<mail>ulm#gmail.com<mail>
</getGenReturn>
/getGenResponse>
Output expected
<?xml version="1.0" encoding="UTF-8"?>
<getGenResponse>
<getGenReturn>
<name> ULM </name>
<mail>ulm#gmail.com<mail>
</getGenReturn>
<getGenReturn>
<name>ABC</name>
<mail>abc#gmail.com<mail>
</getGenReturn>
/getGenResponse>
At you moment all you are doing is testing a multiRef element exists, and outputting only one new getGenReturn element.
All you really need to do is replace the xsl:if with xsl:for-each to select all the elements, then you will get one getGenReturn for each. And also change the xsl:value-of to use a relative path
<xsl:template match="/">
<xsl:element name="getGenResponse">
<xsl:for-each select="//soap:Body/multiRef">
<xsl:element name="getGenReturn">
<xsl:element name="name"><xsl:value-of select="name"/></xsl:element>
<xsl:element name="mail"><xsl:value-of select="mail"/></xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
Or better still, do this, as xsl:element is not really needed here if you are using static names
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
Note, you don't actually need the identity template in this case. Try this XSLT:
<xsl:stylesheet version="1.0" xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="soap response">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="soap response">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
</xsl:stylesheet>

Very new to XSLT! Having difficulty adding new line between records

I have a soap xml output and need to convert it to plain text file. I am trying to use xsltproc. Got the following xsl tempalate online
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:csv="csv:csv">
<xsl:output method="text" encoding="utf-8" />
<xsl:strip-space elements="*" />
<xsl:variable name="delimiter" select="'|'" />
<csv:columns><column>Numbers</column></csv:columns>
<xsl:template match="getNumbersResponse">
<xsl:variable name="property" select="." />
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
My soap xml output is as follows
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body>
<ns4:getNumbersResponse xmlns:ns4="http://service.engine.com"><ns4:Numbers>100</ns4:Numbers>
<ns4:Numbers>200</ns4:Numbers>
</ns4:getNumbersResponse>
</soapenv:Body>
</soapenv:Envelope>
When I try xsltproc using the above xsl tempalate to transform this xml output, I get records in following format
100200
I want to add a new line between each record. Found online that adding following line should do it but I do not see any changes in the output with or without this line in xsl template.
<xsl:text>
</xsl:text>
I would want my output to be like this
Numbers|
100|
200|
Your stylesheet doesn't actually do anything, because your only template does not match anything in the source XML. The output you see is purely the result of the built-in template rules.
If you want to get a return-separated list of the ns4:Numbers values, you should do:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns4="http://service.engine.com">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="/soapenv:Envelope">
<xsl:for-each select="soapenv:Body/ns4:getNumbersResponse/ns4:Numbers">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note the use of declared prefixes to address the nodes in your XML.
To get the result in your edited question, do:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns4="http://service.engine.com">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="/soapenv:Envelope">
<xsl:text>Numbers|
</xsl:text>
<xsl:for-each select="soapenv:Body/ns4:getNumbersResponse/ns4:Numbers">
<xsl:value-of select="."/>
<xsl:text>|
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Filemaker xml output via xslt with column names

I am new to xslt programming and xlm. I have created the code below this works fine, except that instead variable names for each column, it just shows "colno" How do I get the column names into the output?
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fmp="http://www.filemaker.com/fmpxmlresult"
exclude-result-prefixes="fmp"
>
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:variable name="kMetaData" select="fmp:METADATA/fmp:FIELD"/>
<xsl:variable name="colno"
select="count($kMetaData[following-sibling::fmp:FIELD/#NAME]) + 1" />
<xsl:template match="/fmp:FMPXMLRESULT">
<PERSON>
<xsl:apply-templates select="fmp:RESULTSET/fmp:ROW" />
</PERSON>
</xsl:template>
<xsl:template match="fmp:ROW">
<ELEMENTS>
<xsl:apply-templates select="fmp:COL" />
</ELEMENTS>
</xsl:template>
<xsl:template match="fmp:COL">
<xsl:element name="colno">
<xsl:value-of select="fmp:DATA" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It is hard to make some suggestion without input xml. But at first sight this <xsl:element name="colno"> says "output an element <colno>". I think you should use something like <xsl:element name="{xpath/to/columnName}">
edit:
According to your input xml your template for "COL" element should look like
<xsl:template match="COL">
<xsl:variable name="colPosition" select="position()" />
<!-- Prevent spaces in NAME attribute of FIELD element -->
<xsl:variable name="colName" select="translate($kMetaData[$colPosition]/#NAME, ' ', '_')" />
<xsl:element name="{$colName}">
<xsl:value-of select="DATA"/>
</xsl:element>
</xsl:template>
Then the output looks like
<?xml version="1.0" encoding="utf-8"?>
<PERSON>
<ELEMENTS>
<FIRSTNAME>Richard</FIRSTNAME>
<LASTNAME>Katz</LASTNAME>
<MIDDLENAME>David</MIDDLENAME>
<REQUESTDT>1/1/2001</REQUESTDT>
<salutation>Mr</salutation>
<Bargaining_Unit>CSEA (02,03,04)</Bargaining_Unit>
<Field_134>b</Field_134>
</ELEMENTS>
</PERSON>