How can i replace an attribute in xml using xsl transformation, depending on its value.
For example, if there is such xml
<Text Style='style1'>
...
</Text>
transform it to
<Text Font='Arial' Bold='true' Color='Red'>
...
</Text>
For Style='style2' set another attributes and values, for example Font='Sans' Italic='true'.
One posible way: ussing attribute sets. This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:attribute-set name="style1">
<xsl:attribute name="Font">Arial</xsl:attribute>
<xsl:attribute name="Bold">true</xsl:attribute>
<xsl:attribute name="Color">Red</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="style2">
<xsl:attribute name="Font">Sans</xsl:attribute>
<xsl:attribute name="Italic">true</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="Text[#Style='style1']">
<xsl:copy use-attribute-sets="style1">
<xsl:copy-of select="#*[name()!='Style']"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="Text[#Style='style2']">
<xsl:copy use-attribute-sets="style2">
<xsl:copy-of select="#*[name()!='Style']"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With this input:
<root>
<Text Style='style1'></Text>
<Text Style='style2'></Text>
</root>
Output:
<Text Font="Arial" Bold="true" Color="Red"></Text>
<Text Font="Sans" Italic="true"></Text>
Other way: inline "attribute sets". This stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my"
exclude-result-prefixes="my">
<xsl:output indent="yes"/>
<my:style1 Font="Arial" Bold="true" Color="Red"/>
<my:style2 Font="Sans" Italic="true"/>
<xsl:template match="Text[#Style]">
<xsl:copy>
<xsl:copy-of select="document('')/*/my:*
[local-name()=current()/#Style]/#*"/>
<xsl:copy-of select="#*[name()!='Style']"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You will need to have some kind of rule that you use to convert the style from one to the other. Making the assumption you are inputting html you will need something like.
<xsl:template match="#* | node()">
<xsl:choose>
<xsl:when test="local-name() = 'Style'">
<xsl:apply-templates select="." mode="Style" />
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#Style" mode="Style">
<xsl:choose>
<xsl:when test="node() = 'style1'">
<xsl:attribute name="Font">Arial</xsl:attribute>
<xsl:attribute name="Bold">true</xsl:attribute>
<xsl:attribute name="Color">Red</xsl:attribute>
</xsl:when>
<xsl:when test="node() = 'style2'">
<xsl:attribute name="Font">Sans</xsl:attribute>
<xsl:attribute name="Bold">true</xsl:attribute>
</xsl:when>
</xsl:choose>
</xsl:template>
Related
I have a XML file where elements B are inside elements A and I want to move them up. From:
<?xml version="1.0" encoding="utf-8"?>
<root>
<A>
<C>Text</C>
Text again
More text
<D>Other text</D>
<B>Text again</B>
<C>No</C>
<D>May be</D>
<B>What?</B>
</A>
<A>
Something
<B>Nothing</B>
<D>Again</D>
<B>Content</B>
End
</A>
</root>
I would like to have:
<?xml version="1.0" encoding="utf-8"?>
<root>
<A>
<C>Text</C>
Text again
More text
<D>Other text</D>
</A>
<B>Text again</B>
<A>
<C>No</C>
<D>May be</D>
</A>
<B>What?</B>
<A>
Something
</A>
<B>Nothing</B>
<A>
<D>Again</D>
</A>
<B>Content</B>
<A>
End
</A>
</root>
The closest XSLT program I have is this:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" 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="A">
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="name()='B'">
<xsl:apply-templates select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="A">
<xsl:apply-templates select="."/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
It has two problems: it ignores text nodes (this is probably just a matter of adding |text() to the select="*") but, more important, it creates a element for each node while I would like them to stay together under one . For instance, the above stylesheet makes:
<A><C>No</C></A>
<A><D>May be</D></A>
where I want:
<A><C>No</C>
<D>May be</D></A>
In my XML files, are always direct children of , and there is no or nesting.
The main use case is producing HTML where UL and OL cannot be inside a P.
This question is related but not identical to xslt flattening out child elements in a DocBook para element (and may be also to Flatten xml hierarchy using XSLT
)
As I said in the comment to your question, this is not about moving elements up in hierarchy. It is about grouping nodes, and creating a new parent A element for each group determined by the dividing B element.
In XSLT 1.0 this can be achieved using a so-called sibling recursion:
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="/root">
<xsl:copy>
<xsl:apply-templates select="A"/>
</xsl:copy>
</xsl:template>
<xsl:template match="A">
<xsl:copy>
<xsl:apply-templates select="node()[1][not(self::B)]" mode="sibling"/>
</xsl:copy>
<xsl:apply-templates select="B[1]" mode="sibling"/>
</xsl:template>
<xsl:template match="node()" mode="sibling">
<xsl:copy-of select="." />
<xsl:apply-templates select="following-sibling::node()[1][not(self::B)]" mode="sibling"/>
</xsl:template>
<xsl:template match="B" mode="sibling">
<xsl:copy-of select="." />
<xsl:if test="following-sibling::node()[normalize-space()]">
<A>
<xsl:apply-templates select="following-sibling::node()[1][not(self::B)]" mode="sibling"/>
</A>
<xsl:apply-templates select="following-sibling::B[1]" mode="sibling"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
An XSLT-1.0 solution - which is quite ugly - is the following. The output is as desired, but only for this simple MCVE. A general solution would be far more complicated as #michael.hor257k mentioned in the comments. Without more data it is unlikely to create a better solution in XSLT-1.0. Solutions for XSLT-2.0 and above may simplify this.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/root">
<xsl:copy>
<xsl:for-each select="A">
<xsl:if test="normalize-space(text()[1])">
<A>
<xsl:copy-of select="text()[1]" />
</A>
</xsl:if>
<xsl:if test="preceding::*">
<xsl:copy-of select="B[1]" />
</xsl:if>
<A>
<xsl:copy-of select="C[1] | C[1]/following-sibling::text()[1] | D[1]" />
</A>
<xsl:if test="not(preceding::*)">
<xsl:copy-of select="B[1]" />
</xsl:if>
<A>
<xsl:copy-of select="C[2] | C[2]/following-sibling::text()[1]" />
<xsl:if test="D[2]">
<xsl:copy-of select="D[2]" />
</xsl:if>
</A>
<xsl:copy-of select="B[2]" />
<xsl:if test="normalize-space(text()[last()])">
<A>
<xsl:copy-of select="text()[last()]" />
</A>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Concerning the situation of
<A><C>No</C></A>
<A><D>May be</D></A>
It is handled appropriately in the above code. So its output is
<A>
<C>No</C>
<D>May be</D>
</A>
Easy in XSLT 2 or 3 with group-adjacent=". instance of element(B)" or group-adjacent="boolean(self::B)", here is an XSLT 3 example (XSLT 3 is supported by Saxon 9.8 or 9.9 on Java and .NET (https://sourceforge.net/projects/saxon/files/Saxon-HE/) and by Altova since 2017 releases):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="A">
<xsl:for-each-group select="node()" group-adjacent=". instance of element(B)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy select="..">
<xsl:apply-templates select="current-group()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/gWmuiKv
In XSLT 2 you need to spell out the <xsl:mode on-no-match="shallow-copy"/> as the identity transformation template and use xsl:element instead xsl:copy:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="A">
<xsl:for-each-group select="node()" group-adjacent=". instance of element(B)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{name(..)}" namespace="{namespace-uri(..)}">
<xsl:apply-templates select="current-group()"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:transform>
http://xsltransform.hikmatu.com/pPqsHT2
I doing an XSLT transformation.
input message:
<Accounts operation="query">
<Account operation="query">
<Home_spcPage>google.com</Home_spcPage>
<Id>1-NP8S</Id>
</Account>
</Accounts>
which should get transformed to :
<ipString>
<![CDATA[<Accounts operation="update" boNameVar="Account" bcNameVar="Account">
<Account operation="update">
<Home_spcPage>google.com</Home_spcPage>
<Id>1-NP8S</Id>
</Account>
</Accounts>]]>
</ipString>
I am trying with the below XSLT.
<xsl:stylesheet xmlns:crma="www.c123.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes" method="xml"/>
<xsl:variable name="messageBlock">
<xsl:call-template name="main"/>
</xsl:variable>
<xsl:template match="/" name="main">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:variable name="level" select="count(ancestor::node())"/>
<xsl:copy>
<xsl:choose>
<xsl:when test="$level=2">
<xsl:attribute name="operation">
<xsl:value-of select="'update'"/>
</xsl:attribute>
<xsl:variable name="currNodeVar" select="name()"/>
<xsl:if test="$currNodeVar='Account'">
<xsl:attribute name="boNameVar">Account</xsl:attribute>
<xsl:attribute name="bcNameVar">Account</xsl:attribute>
</xsl:if>
<xsl:if test="$currNodeVar='Contact'">
<xsl:attribute name="boNameVar">Contact</xsl:attribute>
<xsl:attribute name="bcNameVar">Contact</xsl:attribute>
</xsl:if>
</xsl:when>
<xsl:when test="$level=4">
<xsl:attribute name="operation">
<xsl:value-of select="'update'"/>
</xsl:attribute>
<xsl:variable name="currBCNameVar" select="name()"/>
<xsl:variable name="parBCNameVar" select="name(../..)"/>
</xsl:when>
</xsl:choose>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:copy-of select="$messageBlock"/>
</xsl:template>
<xsl:template match="/">
<xsl:element name="ipString">
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:copy-of select="$messageBlock"/>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
but not getting the desired results . It seems like the XSLT is not considering the entire XML.
Any help is appreciated .
Thanks,
Naveen
I changed now multiple times my xsl, but I didn't find the right way. Now here is what I want:
I try to find now all missing Pages, which got no description. If it's missing, I want to add a description for that page, if it exist, I want to modify the Description. The String for pageX to pageXDescription is always the same.
Here is my short example xml:
<BOOK>
<PAGE NAME='page1' VALUE='coolText'/>
<PAGE NAME='Description1' VALUE='coolDescription'/>
<PAGE NAME='page2' VALUE='moreText'/>
<PAGE NAME='page3' VALUE='aLotMoreText'/>
<PAGE NAME='Description3' VALUE='aLotMoreDescriptions'/>
</BOOK>
I tried it with something like that:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- define output settings and header -->
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" media-type="string" encoding="ISO-8859-1" doctype-system="deftable.dtd"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="BOOK[PAGE/#NAME='page1']">
<xsl:copy>
<xsl:call-template name="create_missing_description_pages">
<xsl:with-param name="page" select="'page1'"/>
<xsl:with-param name="description" select="'Description1'"/>
<xsl:with-param name="new_description" select="'newContent'"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template match="BOOK[PAGE/#NAME='page2']">
<xsl:copy>
<xsl:call-template name="create_missing_description_pages">
<xsl:with-param name="page" select="'page2'"/>
<xsl:with-param name="description" select="'Description2'"/>
<xsl:with-param name="new_description" select="'newContent'"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template match="BOOK[PAGE/#NAME='page3']">
<xsl:copy>
<xsl:call-template name="create_missing_description_pages">
<xsl:with-param name="page" select="'page3'"/>
<xsl:with-param name="description" select="'Description3'"/>
<xsl:with-param name="new_description" select="'newContent'"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<!-- Function to generate missing XML Tags -->
<xsl:template name="create_missing_description_pages">
<xsl:param name="page"/>
<xsl:param name="description"/>
<xsl:param name="new_description"/>
<xsl:apply-templates select="#*|VARIABLE[#NAME=$page]/preceding-sibling::node()"/>
<xsl:apply-templates select="VARIABLE[#NAME=$page]"/>
<xsl:if test="not(VARIABLE/#NAME=$description)">
<xsl:element name="PAGE">
<xsl:attribute name="NAME"><xsl:value-of select="$description"/></xsl:attribute>
<xsl:attribute name="VALUE"><xsl:value-of select="$new_description"/></xsl:attribute>
</xsl:element>
</xsl:if>
<xsl:apply-templates select="VARIABLE[#NAME=$page]/following-sibling::node()"/>
</xsl:template>
<xsl:template match="BOOK/PAGE">
<xsl:copy>
<xsl:choose>
<xsl:when test="#NAME='Description1'">
<xsl:attribute name="NAME"><xsl:value-of select="#NAME"/></xsl:attribute>
<xsl:attribute name="VALUE">newContent</xsl:attribute>
</xsl:when>
<xsl:when test="#NAME='Description2'">
<xsl:attribute name="NAME"><xsl:value-of select="#NAME"/></xsl:attribute>
<xsl:attribute name="VALUE">newContent</xsl:attribute>
</xsl:when>
<xsl:when test="#NAME='page3Description'">
<xsl:attribute name="NAME"><xsl:value-of select="#NAME"/></xsl:attribute>
<xsl:attribute name="VALUE">newContent</xsl:attribute>
</xsl:when>
<!-- other child items will just be copied -->
<xsl:otherwise>
<xsl:copy-of select="#*"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
That is what I'm expecting in all cases, if all Descriptions are missing, or if all Descriptions are available:
<BOOK>
<PAGE NAME='page1' VALUE='coolText'/>
<PAGE NAME='Description1' VALUE='newContent'/>
<PAGE NAME='page2' VALUE='moreText'/>
<PAGE NAME='Description2' VALUE='newContent'/>
<PAGE NAME='page3' VALUE='aLotMoreText'/>
<PAGE NAME='Description3' VALUE='newContent'/>
</BOOK>
If there is no page3, then I want also no page Description.
I hope so, that it's understandable.
Thanks a lot for a hint, where my logical failure is and how to fix it.
Best regards
Björn
Couldn't you make this simply:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<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="PAGE">
<xsl:variable name="pagenum" select="xs:integer(substring-after(#NAME, 'page'))" />
<xsl:copy-of select="."/>
<PAGE NAME='Description{$pagenum}'>
<xsl:attribute name="VALUE">
<xsl:choose>
<xsl:when test="$pagenum=1">newContent1</xsl:when>
<xsl:when test="$pagenum=2">newContent2</xsl:when>
<xsl:when test="$pagenum=3">newContent3</xsl:when>
</xsl:choose>
</xsl:attribute>
</PAGE>
</xsl:template>
<xsl:template match="PAGE[starts-with(#NAME, 'Description')]"/>
</xsl:stylesheet>
Is there a way to convert these elsevier tags into mml:math tags?
<ce:chem>PEG<ce:inf>BOUND</ce:inf>
<ce:hsp sp="0.25"/>=<ce:hsp sp="0.25"/>PEG<ce:inf>TOT</ce:inf>
<ce:hsp sp="0.25"/>-<ce:hsp sp="0.25"/>PEG<ce:inf>FINAL</ce:inf>
</ce:chem>
Try this:
XML: (Ensure ce:chem content should not have line breaks and comment text. grouping functions can do better than my code, but I placed this jsut to meet the requirement)
<article xmlns:ce="http://www.elsevier.com/xml/common/dtd"
xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mml="http://www.w3.org/1998/Math/MathML">
<p>
<ce:chem>PEG<ce:inf>BOUND</ce:inf><ce:hsp sp="0.25"/>=<ce:hsp sp="0.25"/>PEG<ce:inf>TOT</ce:inf><ce:hsp sp="0.25"/>-<ce:hsp sp="0.25"/>PEG<ce:inf>FINAL</ce:inf>A<ce:sup>2</ce:sup></ce:chem>
</p>
</article>
XSLT 2.0: (latest xslt)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ce="http://www.elsevier.com/xml/common/dtd"
xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mml="http://www.w3.org/1998/Math/MathML"
xmlns:ja="http://www.elsevier.com/xml/ja/dtd">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()"><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:template>
<xsl:template match="ce:chem">
<xsl:copy>
<xsl:apply-templates select="node()[not(self::ce:inf)][not(self::ce:sup)] | #*" />
</xsl:copy>
</xsl:template>
<xsl:key name="ksub" match="ce:inf" use="generate-id(preceding-sibling::node()[1][self::text()])"/>
<xsl:key name="ksup" match="ce:sup" use="generate-id(preceding-sibling::node()[1][self::text()])"/>
<xsl:template match="ce:chem/text()">
<xsl:choose>
<xsl:when test="following-sibling::node()[1][name()='ce:inf']">
<xsl:element name="ce:msub">
<xsl:call-template name="tempNameElements"/>
<xsl:apply-templates select="key('ksub', generate-id())" />
</xsl:element>
</xsl:when>
<xsl:when test="following-sibling::node()[1][name()='ce:sup']">
<xsl:element name="ce:msup">
<xsl:call-template name="tempNameElements"/>
<xsl:apply-templates select="key('ksup', generate-id())" />
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="tempNameElements"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="ce:hsp">
<xsl:element name="mml:mspace">
<xsl:attribute name="width"><xsl:value-of select="#sp"/></xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template match="ce:inf">
<xsl:for-each select="node()">
<xsl:call-template name="tempNameElements"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="ce:sup">
<xsl:for-each select="node()">
<xsl:call-template name="tempNameElements"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="tempNameElements">
<xsl:choose>
<xsl:when test="starts-with(replace(self::text(), '([A-z]+)', 'A'), 'A')">
<xsl:element name="mml:mi"><xsl:value-of select="."/></xsl:element>
</xsl:when>
<xsl:when test="starts-with(replace(self::text(), '([0-9]+)', '9'), '9')">
<xsl:element name="mml:mn"><xsl:value-of select="."/></xsl:element>
</xsl:when>
<xsl:when test="matches(self::text(), '^(\(|\[|\{|=|\-)$')">
<xsl:element name="mml:mo"><xsl:value-of select="."/></xsl:element>
</xsl:when>
<xsl:otherwise><xsl:element name="mml:mtext"><xsl:value-of select="."/></xsl:element></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<article xmlns:ce="http://www.elsevier.com/xml/common/dtd"
xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mml="http://www.w3.org/1998/Math/MathML">
<p>
<ce:chem>
<ce:msub>
<mml:mi>PEG</mml:mi>
<mml:mi>BOUND</mml:mi>
</ce:msub>
<mml:mspace width="0.25"/>
<mml:mo>=</mml:mo>
<mml:mspace width="0.25"/>
<ce:msub>
<mml:mi>PEG</mml:mi>
<mml:mi>TOT</mml:mi>
</ce:msub>
<mml:mspace width="0.25"/>
<mml:mo>-</mml:mo>
<mml:mspace width="0.25"/>
<ce:msub>
<mml:mi>PEG</mml:mi>
<mml:mi>FINAL</mml:mi>
</ce:msub>
<ce:msup>
<mml:mi>A</mml:mi>
<mml:mn>2</mml:mn>
</ce:msup>
</ce:chem>
</p>
</article>
We have a program that uses xml to save configurations of our program. Someone decided to rename a couple of values in our database and these renames should now also be backwards compatible in the configurations of our customers.
An example of a configuration
<configuration>
<fruitToEat>yellow_curved_thing</fruitToEat> <!-- should now become banana -->
</configuration>
A simple match would be (not tested, just an example):
<xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match"/configuration/fruitToEat/text()">
<xsl:text>banana</xsl:text>
</xsl:template>
</xsl:template>
But this is just one example and I want to do this 150 times.
Is it possible to make an xsl that reads a simple text file or ini file that tells me how the 150 matches should look alike?
<xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- recreate this template 150 times from an ini file or something -->
<xsl:template match"/configuration/fruitToEat/text()[.='yellow_curved_thing']">
<xsl:text>banana</xsl:text>
</xsl:template>
</xsl:template>
An example of my mapping file could be simply:
yellow_curved_thing = banana
round_thing = tomato
round_dotted = strawberry
And I would simply want a small xslt that tells me:
<xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- recreate this template 150 times from an ini file or something -->
<xsl:template match"/configuration/fruitToEat/text()[.=$fileRow0]">
<xsl:text>$fileRow1</xsl:text>
</xsl:template>
</xsl:template>
So even if I think there is more complexity behind the curtain, may be this will help a little bit.
There are some possibilities to do this with xlst. Which would be best in long term depends on complexity in real life and how often you need to do this with different "mapping" information.
For your easy example you can put the "mapping" information into a xml file. This could be done by some script form ini file.
<mappings>
<mapping name="fruitToEat" >
<map from="yellow_curved_thing" to="banana" />
<map from="round_thing" to="tomato" />
<map from="round_dotted" to="strawberry" />
</mapping>
</mappings>
Than you can have a template which make use of this mapping information:
<xsl:variable name="fruitMapping"
select="document('fruitmapping.xml')//mapping[#name='fruitToEat']" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/configuration/fruitToEat/text()" >
<!-- find the entry in "ini file" -->
<xsl:variable name ="map" select="$fruitMapping/map[#from = current()]" />
<xsl:choose>
<xsl:when test="$map" >
<xsl:value-of select="$map/#to"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
But if this is a onetime job I would implement this "mapping" direct a template. Like this:
<xsl:template match="/configuration/fruitToEat/text()" >
<xsl:choose>
<xsl:when test=".='yellow_curved_thing'" >banana</xsl:when>
<xsl:when test=".='round_thing'" >tomato</xsl:when>
<xsl:when test=".='round_dotted'" >strawberry</xsl:when>
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
It seems you wanted to create your XSLT dynamic XSLT on the basis of your configuration file which is also an XML document. Have a look this exmple considering this:
configuration.xml
<p>
<configuration>
<fruitToEat>yellow_curved_thing</fruitToEat>
<mapped>banana</mapped>
</configuration>
<configuration>
<fruitToEat>round_thing</fruitToEat>
<mapped>tomato</mapped>
</configuration>
</p>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:element name="xsl:stylesheet">
<xsl:attribute name="version">
<xsl:text>1.0</xsl:text>
</xsl:attribute>
<xsl:element name="xsl:template">
<xsl:attribute name="match">
<xsl:text>node()|#*</xsl:text>
</xsl:attribute>
<xsl:element name="xsl:copy">
<xsl:element name="xsl:apply-templates">
<xsl:attribute name="select">
<xsl:text>node()|#*</xsl:text>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:element>
<xsl:for-each select="//configuration">
<xsl:element name="xsl:template">
<xsl:attribute name="match">
<xsl:text>configuration/fruitToEat/text()[.=</xsl:text>
<xsl:text>'</xsl:text>
<xsl:value-of select="fruitToEat"/>
<xsl:text>']</xsl:text>
</xsl:attribute>
<xsl:element name="xsl:text">
<xsl:value-of select="mapped"/>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
output:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="configuration/fruitToEat/text()[.='yellow_curved_thing']">
<xsl:text>banana</xsl:text>
</xsl:template>
<xsl:template match="configuration/fruitToEat/text()[.='round_thing']">
<xsl:text>tomato</xsl:text>
</xsl:template>
</xsl:stylesheet>