XLST 1.0 Split node - xslt-1.0

I want to split this node at <foo>:
<p>
<span>This is before foo.</span>
<foo>inside foo</foo>
<span>This is after foo.</span>
</p>
The result I want is
<p>
<span>This is before foo.</span>
</p>
<foo>inside foo</foo>
<p>
<span>This is after foo.</span>
</p>
But I'm getting
<p>
<span>This is before foo.</span>
<p>
<span>This is before foo.</span>
</p>
<foo>inside foo</foo>
<p>
<span>This is after foo.</span>
</p>
<span>This is after foo.</span>
</p>
And here's the style sheet I'm trying with:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="p/foo">
<xsl:element name="p">
<xsl:apply-templates select="preceding-sibling::node()"/>
</xsl:element>
<foo>
<xsl:apply-templates/>
</foo>
<xsl:element name="p">
<xsl:apply-templates select="following-sibling::node()"/>
</xsl:element>
</xsl:template>
</xsl:transform>
How can I get rid of that extra output?
Edit: Gave a more realistic example. See http://xsltransform.net/pNP88xQ/2

-- edited in response to your edited question --
This is not an easy task to accomplish in XSLT 1.0 (it's much easier to do in XSLT 2.0 using xsl:for-each-group).
One possible approach is a method known as 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" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<!-- start sibling recursion -->
<xsl:apply-templates select="node()[1]"/>
</xsl:template>
<!-- first node in a group -->
<xsl:template match="p/node()[not(self::foo)]">
<p>
<xsl:call-template name="identity"/>
<!-- collect the following node in this group -->
<xsl:apply-templates select="following-sibling::node()[1][not(self::foo)]" mode="collect"/>
</p>
<!-- continue recursion with the following divider -->
<xsl:apply-templates select="following-sibling::foo[1]"/>
</xsl:template>
<!-- other nodes in a group -->
<xsl:template match="node()" mode="collect">
<xsl:call-template name="identity"/>
<!-- collect the following node in this group -->
<xsl:apply-templates select="following-sibling::node()[1][not(self::foo)]" mode="collect"/>
</xsl:template>
<xsl:template match="foo">
<xsl:call-template name="identity"/>
<!-- restart sibling recursion -->
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
</xsl:stylesheet>

Related

Move element to parent

I am new to XSLT, i want to move a element with all its child elements to the root as mentioned below:
Input XML
<a name = "test">
<b name = "test1">
<c name = "test2">
<c1>Dummy</c1>
</c>
</b>
</a>
Expected:
<a name = "test">
<b name = "test1">
</b>
<c name = "test2">
<c1>Dummy</c1>
</c>
</a>
Try this. (This is called push programming.)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="UTF-8" />
<xsl:template match="a">
<xsl:copy>
<xsl:apply-templates select="#*|b"/>
<xsl:apply-templates select="b/c"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:copy>
<!-- Don't do an apply template with c in it. Just get the attributes for b. The c node pushed from the a template gets handled by the identity. -->
<xsl:apply-templates select="#*"/>
</xsl:copy>
</xsl:template>
<!-- Identity template. -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

How to remain space in XSL

I have an XML of the following process
<p>
<contribution>
<authors><author>
<surname>Zhengxing</surname> <given-name>Chen</given-name>
</author> </authors>
</contribution>
</p>
But I need below format. Major problem is in space between surname and given-name. But after converting the file, space is gone.
<p>
<span class="contribution">
<span class="authors"><span class="author">
<span class="surname">Zhengxing</span> <span class="given-name">Chen</span>
</span> </span>
</span>
</p>
Thanks for advance.
Try this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<xsl:element name="p"><xsl:apply-templates/></xsl:element>
</xsl:template>
<xsl:template match="contribution|authors|author|surname|given-name">
<xsl:element name="span">
<xsl:attribute name="class"><xsl:value-of select="name()"/></xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Looking to insert div into html body with xsl 1.0

Pretty simple: What I'm looking for a simple XSL to change
<body>
...
</body>
To
<body>
...
<div>...</div>
</body>
<xsl:template match="body">
<xsl:apply-templates />
<div>...</div>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="curTagName" select="name()"/>
<xsl:element name="{$curTagName}">
<!-- Walk through the attributes -->
<xsl:apply-templates select="#*">
<xsl:sort select="name()"/>
</xsl:apply-templates>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:variable name="curAttName" select="name()"/>
<xsl:attribute name="{$curAttName}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>

XSLT Attribute match is not allowed on this element

I'm getting Attribute match is not allowed on this element errors on all these elements XAML except the root. I think I'm missing something about the syntax...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates match="/Table"/>
<xsl:apply-templates match="/Paragraph"/>
</body>
</html>
</xsl:template>
<xsl:template match="Table">
<table>
<xsl:apply-templates match="TableRowGroup"/>
</table>
</xsl:template>
<xsl:template match="TableRowGroup">
<xsl:apply-templates match="TableRow"/>
</xsl:template>
<xsl:template match="TableRow">
<tr>
<xsl:apply-templates match="TableCell"/>
</tr>
</xsl:template>
<xsl:template match="TableCell">
<td>
</td>
</xsl:template>
</xsl:stylesheet>
Use <xsl:apply-templates select="..."/> instead of <xsl:apply-templates match="..."/>.

XAML XSLT attributes fail to match

When I try to transform this XAML document
<Section xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve" TextAlignment="Left" LineHeight="Auto" IsHyphenationEnabled="False" xml:lang="en-us" FlowDirection="LeftToRight" NumberSubstitution.CultureSource="User" NumberSubstitution.Substitution="AsCulture" FontFamily="Arial" FontStyle="Normal" FontWeight="Normal" FontStretch="Normal" FontSize="12" Foreground="#FF000000" Typography.StandardLigatures="True" Typography.ContextualLigatures="True" Typography.DiscretionaryLigatures="False" Typography.HistoricalLigatures="False" Typography.AnnotationAlternates="0" Typography.ContextualAlternates="True" Typography.HistoricalForms="False" Typography.Kerning="True" Typography.CapitalSpacing="False" Typography.CaseSensitiveForms="False" Typography.StylisticSet1="False" Typography.StylisticSet2="False" Typography.StylisticSet3="False" Typography.StylisticSet4="False" Typography.StylisticSet5="False" Typography.StylisticSet6="False" Typography.StylisticSet7="False" Typography.StylisticSet8="False" Typography.StylisticSet9="False" Typography.StylisticSet10="False" Typography.StylisticSet11="False" Typography.StylisticSet12="False" Typography.StylisticSet13="False" Typography.StylisticSet14="False" Typography.StylisticSet15="False" Typography.StylisticSet16="False" Typography.StylisticSet17="False" Typography.StylisticSet18="False" Typography.StylisticSet19="False" Typography.StylisticSet20="False" Typography.Fraction="Normal" Typography.SlashedZero="False" Typography.MathematicalGreek="False" Typography.EastAsianExpertForms="False" Typography.Variants="Normal" Typography.Capitals="Normal" Typography.NumeralStyle="Normal" Typography.NumeralAlignment="Normal" Typography.EastAsianWidths="Normal" Typography.EastAsianLanguage="Normal" Typography.StandardSwashes="0" Typography.ContextualSwashes="0" Typography.StylisticAlternates="0">
<Table CellSpacing="1" Margin="0,0,0,0"><Table.Columns><TableColumn Width="264" /></Table.Columns><TableRowGroup><TableRow><TableCell Padding="0,0,0,0">
<Paragraph FontFamily="Times New Roman" FontSize="10.666666666666666" Margin="0,0,0,0">
<Span FontWeight="Bold"><Run>some text</Run></Span><Run> </Run>
<Span Foreground="#FF00681C"><Run>some more text</Run></Span>
</Paragraph>
</TableCell></TableRow></TableRowGroup></Table>
</Section>
using this XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="/Table"/>
<xsl:apply-templates select="/Paragraph"/>
<xsl:apply-templates select="/Run"/>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="Table">
<table>
<xsl:apply-templates select="TableRowGroup"/>
</table>
</xsl:template>
<xsl:template match="TableRowGroup">
<xsl:apply-templates select="TableRow"/>
</xsl:template>
<xsl:template match="TableRow">
<tr>
<xsl:apply-templates select="TableCell"/>
</tr>
</xsl:template>
<xsl:template match="TableCell">
<td>
</td>
</xsl:template>
<xsl:template match="Paragraph">
<p>
<xsl:apply-templates select=""/>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="Run">
<span>
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="Span">
<span>
<xsl:apply-templates/>
</span>
</xsl:template>
</xsl:stylesheet>
i get this incorrect result
<html>
<body>
some text some more text
</body>
I saw a few problems with your XSLT that would stop it working.
There is no namespace in the XSLT. There needs to be a namespace that
matches the one in the input XML, and you need to use this namespace
when matching elements.
Your first template matches "/", which matches the root node. I
assume you actually want to match the document element Section. The
apply-templates inside this are matching elements beginning with
"/", which makes them absolute paths, so they won't match anything.
I've taken a guess at what output XML you expect. The following XSLT stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
version="1.0"
exclude-result-prefixes="ns">
<xsl:strip-space elements="*" />
<xsl:output indent="yes" method="html"/>
<xsl:template match="/ns:Section">
<html>
<body>
<xsl:apply-templates select="ns:Table"/>
</body>
</html>
</xsl:template>
<xsl:template match="ns:Table">
<table>
<xsl:apply-templates select="ns:TableRowGroup"/>
</table>
</xsl:template>
<xsl:template match="ns:TableRowGroup">
<xsl:apply-templates select="ns:TableRow"/>
</xsl:template>
<xsl:template match="ns:TableRow">
<tr>
<xsl:apply-templates select="ns:TableCell"/>
</tr>
</xsl:template>
<xsl:template match="ns:TableCell">
<td>
<xsl:apply-templates select="ns:Paragraph"/>
</td>
</xsl:template>
<xsl:template match="ns:Paragraph">
<p>
<xsl:apply-templates select="ns:Span"/>
</p>
</xsl:template>
<xsl:template match="ns:Run">
<span>
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="ns:Span">
<span>
<xsl:apply-templates select="ns:Run"/>
</span>
</xsl:template>
</xsl:stylesheet>
produces the following output when applied to your example input XML:
<html>
<body>
<table>
<tr>
<td>
<p><span><span>some text</span></span><span><span>some more text</span></span></p>
</td>
</tr>
</table>
</body>
</html>
which may need a little formatting...