Can you set the position of a particular item in a for-each loop if the value equals something? I tried the below example but it didn't work:
<xsl:choose>
<xsl:when test='name = "Dining"'>
<xsl:value-of select="position()=1"/>
</xsl:when>
<xsl:otherwise>
[Normal position]
</xsl:otherwise>
</xsl:choose>
Dining will always appear at the top of the list and then the list will render as normal.
You haven't provided an example of your input XML, or shown exactly what you want to do with it, so I am guessing a bit. You could try something 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:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="Dining"/>
<xsl:apply-templates select="*[not(self::Dining)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to the following XML:
<root>
<Bathroom />
<Dining />
<Kitchen />
<Bedroom />
</root>
It produces:
<root>
<Dining />
<Bathroom />
<Kitchen />
<Bedroom />
</root>
Related
I'm struggling to get this abomination called XSLT to work. I need to get an EXACT attribute at EXACT path, pass its original value to a template and rewrite this value with the result from the template.
I'm having a file like this:
<?xml version="1.0" encoding="windows-1251"?>
<File>
<Document ReportYear="17">
...
...
</Document>
</File>
So I made an XSLT like this:
<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" encoding="windows-1251" indent="yes" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template name="formatYear">
<xsl:param name="year" />
<xsl:value-of select="$year + 2000" />
</xsl:template>
<xsl:template match="File/Document">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:attribute name="ReportYear">
<xsl:call-template name="formatYear">
<xsl:with-param name="year" select="#ReportYear" />
</xsl:call-template>
</xsl:attribute>
</xsl:copy>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
This works fine except it closes the <Document> tag immediately and places its content immediately after itself.
Also, can I address the ReportYear attribute value without repeating it twice? I tried current() but it didn't work.
If you're closing <xsl:copy> before applying templates to the remainder of the content of <Document>, then of course <Document> will be closed before the remainder of the content of <Document> appears in the output.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" encoding="windows-1251" indent="yes" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Document">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:attribute name="ReportYear">
<xsl:value-of select="#ReportYear + 2000" />
</xsl:attribute>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
outputs
<?xml version="1.0" encoding="windows-1251"?>
<File>
<Document ReportYear="2017">
...
...
</Document>
</File>
I don't think an extra template just for adding 2000 to #ReportYear is necessary. But if you must, you can streamline the whole thing like so
<xsl:template name="formatYear">
<xsl:param name="year" select="#ReportYear" /> <!-- you can define a default value -->
<xsl:value-of select="$year + 2000" />
</xsl:template>
and
<xsl:attribute name="ReportYear">
<xsl:call-template name="formatYear" /> <!-- ...and can use it implicitly here -->
</xsl:attribute>
If you need to process the contents of the Document element with apply-templates and want to keep the result of the applied templates as the children then you need to move the apply-templates inside of the copy:
<xsl:template match="File/Document">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="ReportYear">
<xsl:call-template name="formatYear">
<xsl:with-param name="year" select="#ReportYear"/>
</xsl:call-template>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
Not sure why you haven't simply used
<xsl:template match="File/Document/#ReportYear">
<xsl:attribute name="{name()}">
<xsl:value-of select=". + 2000"/>
</xsl:attribute>
</xsl:template>
together with the identity transformation template.
I have a file called ori.xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<container>
<elA>
<el1>value1</el1>
<el2>value2</el2>
</elA>
<elB>
<el3>value3</el3>
<el4>value4</el4>
<el5>value5</el5>
</elB>
<elC>
<el6>value5</el6>
</elC>
</container>
</root>
and another one called modifs.xml:
<?xml version="1.0" encoding="UTF-8"?>
<els>
<el2>newvalue2</el2>
<el5>newvalue5</el5>
</els>
and I would like to obtain result.xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<container>
<elA>
<el1>value1</el1>
<el2>newvalue2</el2>
</elA>
<elB>
<el3>value3</el3>
<el4>value4</el4>
<el5>newvalue5</el5>
</elB>
<elC>
<el6>value5</el6>
</elC>
</container>
</root>
I'm a beginner in XSLT.
So I started to write a stylesheet with which I'm able to change value2 into newvalue2:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="fileName" select="'modifs.xml'" />
<xsl:param name="modifs" select="document($fileName)" />
<xsl:param name="updateEl" >
<xsl:value-of select="$modifs/els/el2" />
</xsl:param>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//elA/el2">
<xsl:copy>
<xsl:apply-templates select="$updateEl" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
But now I have to modify this stylesheet to be able to know which elements are in modifs.xml and find them in ori.xml. I don't know how to do that. Could you help please ?
I would use a key:
<xsl:key name="ref-change" match="els/*" use="local-name()"/>
<xsl:template match="*[key('ref-change', local-name(), $modifs)]">
<xsl:copy-of select="key('ref-change', local-name(), $modifs)"/>
</xsl:template>
However, using the third argument for the key function is only supported in XSLT 2 and later thus if you use an XSLT 1 processor you need to move the logic into the template, that requires using for-each to "switch" the context document
<xsl:template match="*">
<xsl:variable name="this" select="."/>
<xsl:for-each select="$modifs">
<xsl:choose>
<xsl:when test="key('ref-change', local-name($this))">
<xsl:copy-of select="key('ref-change', local-name($this))"/>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$this">
<xsl:call-template name="identity"/>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
Put name="identity" on your identity transformation template.
i have code *xsl :
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xhtml" indent="yes" />
<xsl:variable name="with" select="'File2.xml'" />
<xsl:template match="/">
<html>
<body>
<h1>WEB Service</h1><br/>
</body>
</html>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
<xsl:variable name="info" select="document($with)/Xml/data[ClassId=current()/ClassId]/." />
<xsl:for-each select="$info/*">
<xsl:if test="name()!='ClassId'">
<xsl:copy-of select="." />
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:transform>
im trying make XHTML, when i running just showing html not include xml fragment..
anyone can help me please ?
thanks
when i running just showing html not include xml fragment..
The reason for this is that the XSLT processor starts by applying the template matching the / root node. This template contains no xsl:apply-templates instructions, so the processing ends here.
Below my input
<root>
<text>
<bold>Co<csc>lorado DivIs</csc>IoN</bold>
</text>
<text>
fi<csc>ve and a ha</csc>lf <x>abc</x>
</text>
</root>
Here is my xslt my implemntation (xslt verions-1.0)
<xsl:for-each select="/root/text">
<xsl:value-of select="./child::*[local-name(.)!='x']" />
<xsl:text> </xsl:text>
</xsl:for-each>
correct output look like below, should ignore only 'x' element value.
Colorado DivIsIoN five and a half
The output I am getting is with missing current element text.
Colorado DivIsIoN ve and a ha
Try it this way?
<xsl:template match="/">
<xsl:for-each select="root/text">
<xsl:for-each select=".//text()[not (parent::x)]">
<xsl:value-of select="." />
</xsl:for-each>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
try this
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match="root">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="text/x"/>
<xsl:template match="#* | node()">
<xsl:apply-templates select="#* | node()"/>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
I have this XML:
<root>
<tab name="Detail">
<section name="mysection">
<items level="1">
<Idx_name>9</Idx_name>
<Type>mytype</Type>
<item name="myname">
<Grams um="(g)">9,0</Grams>
<Pre-infusion>Max</Pre-infusion>
</item>
<Std._Mode>On</Std._Mode>
<Price>100</Price>
</items>
</section>
</tab>
</root>
and this XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items/*">
<xsl:choose>
<xsl:when test="not(name()='item')">
<xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Now, what I want is:
<root>
<tab name="Detail">
<section name="mysection">
<items level="1" Idx_name="9" Type="mytype" Std._Mode="On" Price="100">
<item name="myname">9,0Max</item>
</items>
</section>
</tab>
</root>
I obtain the error: "An attribute cannot be added after a child"
Unluckily, I can not change the order of elements in the node items of my original XML
How can I do it ?
Thanks
Ivan
Make sure you process the elements first you want to transform into attributes e.g. with XSLT 2.0 where you can process sequences which have a order you can simply do
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items">
<xsl:copy>
<xsl:apply-templates select="#*, * except item, item"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items/*[not(self::item)]">
<xsl:attribute name="{name()}" select="."/>
</xsl:template>
<xsl:template match="items/item">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With XSLT 1.0 you would need to spell out several apply-templates in the order you want.