XSLT transformations based on configuration file - xslt

Input:
<chapter xmlns='http://www.w3.org/1998/Math/MathML'>
<math>
<mtext mathcolor="#3e9a3c">This is sample 1</mtext>
<mtext mathcolor="#009bd2">This is sample 2</mtext>
</math>
</chapter>
Output:
<?xml version='1.0' encoding='UTF-8' ?>
<chapter xmlns="http://www.w3.org/1998/Math/MathML"><math>~COLOR~[Green]This is sample 1~COLOR~[Red]This is sample 2</math></chapter>
Configuration file:
<?xml version="1.0"?>
<colors>
<color><mathcolor>#3e9a3c</mathcolor><textcolor>Green</textcolor><colorvalue>1</colorvalue></color>
<color><mathcolor>#009bd2</mathcolor><textcolor>Red</textcolor><colorvalue>2</colorvalue></color>
</colors>
XSLT tried:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:mml="http://www.w3.org/1998/Math/MathML">
<xsl:output method="xml" encoding="UTF-8" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="m:mtext">
<xsl:if test="#mathcolor">
<xsl:choose>
<xsl:when test="#mathcolor='#3e9a3c'"><xsl:text disable-output-escaping="yes">~COLOR~[Green]</xsl:text></xsl:when>
<xsl:when test="#mathcolor='#009bd2'"><xsl:text disable-output-escaping="yes">~COLOR~[Red]</xsl:text></xsl:when>
</xsl:choose>
</xsl:if>
<xsl:apply-templates/></xsl:template>
</xsl:stylesheet>
I am in need to transform the Input given above to the required output based on the configuration file. If the user updates the configuration file, and if the Input has mathcolor attribute value, then its corresponding color should be transformed as shown in output. I can able to use XSLT 1.0. Kindly help to solve this issue
For example:
If the user adds the below coding in the configuration file:
<color><mathcolor>#007c62</mathcolor><textcolor>Magenta</textcolor><colorvalue>3</colorvalue></color> and Input contains mathcolor="#007c62", then Magenta should be applied in the output.

This should work:
Stylesheet
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://www.w3.org/1998/Math/MathML"
xmlns:mml="http://www.w3.org/1998/Math/MathML"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.w3.org/1998/Math/MathML">
<xsl:output method="xml" encoding="UTF-8" indent="no"/>
<xsl:strip-space elements="*"/>
<!--
Config file name/path, can be passed as a parameter to XSL transformation
-->
<xsl:param name="config.file" select="'config.xml'"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!-- <m:mtext> elements with a #mathcolor attribute, no need for <xsl:if> -->
<xsl:template match="m:mtext[#mathcolor]">
<xsl:text>~COLOR~[</xsl:text>
<!--
Fetch color name from $config.file (<color> element with a <mathcolor>
child that has the same value as the #mathcolor attribute of the element
currently being processed)
-->
<xsl:value-of
select="document($config.file)/colors/color
[mathcolor = current()/#mathcolor]/textcolor"/>
<xsl:text>]</xsl:text>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Input
<chapter xmlns='http://www.w3.org/1998/Math/MathML'>
<math>
<mtext mathcolor="#3e9a3c">This is sample 1</mtext>
<mtext mathcolor="#009bd2">This is sample 2</mtext>
</math>
</chapter>
config.xml
<?xml version="1.0"?>
<colors>
<color>
<mathcolor>#3e9a3c</mathcolor>
<textcolor>Green</textcolor>
<colorvalue>1</colorvalue>
</color>
<color>
<mathcolor>#009bd2</mathcolor>
<textcolor>Red</textcolor>
<colorvalue>2</colorvalue>
</color>
</colors>
Output
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://www.w3.org/1998/Math/MathML">
<math>~COLOR~[Green]This is sample 1~COLOR~[Red]This is sample 2</math>
</chapter>

Related

Splitting Multiline XML tag into multiple Nodes

I have a XML below, where new lines are added after each line at Note__c tag. I need to produce the XML by splitting them into multiple Note__c tags.
Input XML-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Note__c>01/15/2020
123456
DFGRTE766
6tgBFR</Note__c>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
Where Note__c contains multiple strings with new line added after each(except the last one)
Expected Output -
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c>123456</Note__c>
<Note__c>DFGRTE766</Note__c>
<Note__c>6tgBFR</Note__c>
</Notes>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
I have written this XSLT but it is missing few tags under the payload element -
<xsl:stylesheet version="2.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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="snotification/data/payload">
<Notes>
<xsl:for-each select="tokenize(Note__c,'\n')">
<Note__c>
<xsl:value-of select="."/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
Output of this-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c> 123456</Note__c>
<Note__c> DFGRTE766</Note__c>
<Note__c> 6tgBFR</Note__c>
</Notes>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
not sure what is missing.
Thanks
Sugata
Change your XSLT to
<xsl:stylesheet version="2.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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="snotification/data/payload/Note__c">
<Notes>
<xsl:for-each select="tokenize(.,'\n')">
<Note__c>
<xsl:value-of select="normalize-space(.)"/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
The output should be as desired.

Move entire xml code inside another xml tag

I have xml code and I want to put two xml tags in the beginning of the xml code so all the code will come under those two tags
Any ideas how to achieve that with XSLT? I am a newby to XSLT and tried the whole day in vain... Any help would really be appreciated.
I have an XML that looks like this
<?xml version="1.0" encoding="UTF-8"?>
<ns0:PCN xmlns:ns0="abc">
<PCD>
<PC>
<TID>123456</TID>
<Sequence>1</Sequence>
<Type>M</Type>
</PC>
<PC>
<TID>123457</TID>
<Sequence>2</Sequence>
<Type>M</Type>
</PC>
</PCD>
</ns0:PCN>
and I need to transform it to look like this:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Messages xmlns:ns0="xyz">
<ns0:Message1>
<ns0:PCN xmlns:ns0="abc">
<PCD>
<PC>
<TID>123456</TID>
<Sequence>1</Sequence>
<Type>M</Type>
</PC>
<PC>
<TID>123457</TID>
<Sequence>2</Sequence>
<Type>M</Type>
</PC>
</PCD>
</ns0:PCN>
</ns0:Message1>
</ns0:Messages>
Please find below my attempted code. This is my first try and I have written this after referring to several codes of xslt . It is not giving me desired result.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Identity transform -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns0:PCN">
<xsl:copy-of select="."/>
<ns0:Messages xmlns:ns0="xyz"/>
<ns0:Message1/>
</xsl:template>
</xsl:stylesheet>
How about simply:
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="/">
<ns0:Messages xmlns:ns0="xyz">
<ns0:Message1>
<xsl:copy-of select="*"/>
</ns0:Message1>
</ns0:Messages>
</xsl:template>
Demo: https://xsltfiddle.liberty-development.net/3NJ38ZK
</xsl:stylesheet>

XSLT transformation : Transfer only 1 of the namespaces

I am new to XSLT transformation and cant seem to get the following result
I have:
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.002.001.03" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.002.001.03 pain.002.001.03.xsd">
<CstmrPmtStsRpt>
need this:
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.002.001.03">
<CstmrPmtStsRpt>
Where the rest of the document should remain the same.
I have put together the following XSLT:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output indent="yes" method="xml" encoding="utf-8" />
<!-- template to copy elements -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#xsi:schemaLocation">
</xsl:template>
<!-- template to copy attributes -->
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<!-- template to copy the rest of the nodes -->
<xsl:template match="comment() | text() | processing-instruction()">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
but get this
<?xml version="1.0" encoding="utf-8"?>
<Document>
<CstmrPmtStsRpt>
Any tips would be appreciated.

XSLT with namespaces: Copy template omits attributes

Applying the standard XSLT copy template,
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
to the JBoss AS 7 standalone.xml, leads to loss of parameters:
<?xml version="1.0" encoding="UTF-8"?><server xmlns="urn:jboss:domain:1.1">
<extensions>
<extension/>
<extension/>
instead of
<?xml version="1.0" encoding="UTF-8"?><server xmlns="urn:jboss:domain:1.1">
<extensions>
<extension module="org.jboss.as.clustering.infinispan"/>
<extension module="org.jboss.as.configadmin"/>
Why?
How can i Make it copy everything?
XSLT transformation is done by Maven XML plugin.
The whole template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ds="urn:jboss:domain:datasources:1.0"
xmlns="urn:jboss:domain:1.1"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*|#*|node()">
<xsl:copy>
<xsl:apply-templates select="*|#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Your template appears to be working correctly.
I modified it and the XML to show that is working:
<?xml version="1.0" encoding="UTF-8"?>
<server xmlns="urn:jboss:domain:1.1">
<extensions>
<extension module="org.jboss.as.clustering.infinispan"/>
<extension module="org.jboss.as.configadmin"/>
</extensions>
</server>
Run with this XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ds="urn:jboss:domain:datasources:1.0"
xmlns:so="urn:jboss:domain:1.1"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*|#*|node()">
<xsl:copy>
<xsl:apply-templates select="*|#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="so:extension">
<xsl:copy>
<xsl:attribute name="testing">just for fun!!</xsl:attribute>
<xsl:copy-of select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Produces:
<?xml version="1.0"?>
<server xmlns="urn:jboss:domain:1.1">
<extensions>
<extension testing="just for fun!!" module="org.jboss.as.clustering.infinispan"/>
<extension testing="just for fun!!" module="org.jboss.as.configadmin"/>
</extensions>
</server>
Here's the output run with your original XSL:
<?xml version="1.0"?>
<server xmlns="urn:jboss:domain:1.1">
<extensions>
<extension module="org.jboss.as.clustering.infinispan"/>
<extension module="org.jboss.as.configadmin"/>
</extensions>
</server>
I downloaded the full JBoss standalone.xml, ran your XSL, and here's the diff of the input and output XML:
so zacharyyoung$ xsltproc so.xsl so.xml > output.xml
so zacharyyoung$ diff so.xml output.xml
1,2c1
< <?xml version='1.0' encoding='UTF-8'?>
<
---
> <?xml version="1.0"?>

XSLT Transformation: Search nodes, and return hierarchical parents

hoping this hasn't been asked before, but I have the following XML:
<Company id="1000" name="Company1000">
<Company id="1020" name="Company1020" />
<Company id="1004" name="Company1004">
<Company id="1005" name="Company1005" />
</Company>
<Company id="1022" name="Company1022" />
</Company>
I have the following XPath to search for nodes: //*[contains(translate(#name, "ABCDEFGHJIKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "005")]
I would like this to return:
<Company id="1000" name="Company1000">
<Company id="1004" name="Company1004">
<Company id="1005" name="Company1005" />
</Company>
</Company>
So this matches the Company1005 node, and all its respective parents. I would like the above to also be returned if I were searching for "100", which in that case, would match each element in turn, but I clearly don't want duplication of nodes.
I've been struggling with this for hours now, so your help will be much appreciated!!!
Thanks.
The solution is to copy only descendant or self nodes which match your requirement (containing the string you want).
Look this picture at the bottom of this page for the XPath axes you need.
Long version:
<?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" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Default: copy everything -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- just copy Company which descendant-or-self contain the matching string -->
<xsl:template match="Company">
<xsl:if test="descendant-or-self::Company[contains(translate(#name, 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '005')]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Short version:
<?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" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Default: copy everything -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- do not copy Company which do not have a descendant-or-self matching string -->
<xsl:template match="Company[not(descendant-or-self::Company[contains(translate(#name, 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '005')])]"/>
</xsl:stylesheet>
Output of your xml:
<?xml version="1.0" encoding="UTF-8"?>
<Company id="1000" name="Company1000">
<Company id="1004" name="Company1004">
<Company id="1005" name="Company1005"/>
</Company>
</Company>