Reputation: 317
I started project in php, using XML, and now I need to apply transformation, so I've discovered XSL for the first time...
I've faced the problem: how to create XSL to do following transformation: - node that starts with "attrib-" to transform to attribute of the parent node
<a1>
<b1>
<attrib-c1>12</attrib-c1>
<c2>23</c2>
</b1>
</a1>
should become:
<a1>
<b1 c1="12">
<c2>23</c2>
</b1>
</a1>
I've started solution like this:
<xsl:stylesheet>
<xsl:output method="xml"/>
<xsl:template match="@*|*|text()">
<xsl:copy>
<xsl:apply-templates select="@*|*|text()"/>
</xsl:copy>
</xsl:template>
...
</xsl:stylesheet>
I would need some help to solve this task. Thanks in advance...
Upvotes: 2
Views: 2443
Reputation: 243449
This is almost the same solution as that of DevNull, but in case there is a conflict between an existing attribute and a new one defined by a child-element, the latter replaces the former:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates mode="attr" select="*[starts-with(name(), 'attrib-')]"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template mode="attr" match="*[starts-with(name(), 'attrib-')]">
<xsl:attribute name="{substring-after(name(), 'attrib-')}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'attrib-')]"/>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<a1>
<b1 existing="attr" x="Y">
<attrib-new>12</attrib-new>
<c2>23</c2>
<attrib-new2>ABCD</attrib-new2>
<attrib-x>Z</attrib-x>
</b1>
</a1>
the wanted, correct result is produced:
<a1>
<b1 existing="attr" x="Z" new="12" new2="ABCD">
<c2>23</c2>
</b1>
</a1>
Upvotes: 1
Reputation: 52858
XML Input (Modified to slightly increase complexity.)
<a1>
<b1 existing="attr">
<attrib-c1>12</attrib-c1>
<c2>23</c2>
<attrib-dh>DevNull</attrib-dh>
</b1>
</a1>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="text()|@*|comment()|processing-instruction()">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="*[starts-with(name(),'attrib')]" mode="attr"/>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(),'attrib')]"/>
<xsl:template match="*" mode="attr">
<xsl:attribute name="{substring-after(name(),'-')}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
XML Output
<a1>
<b1 c1="12" dh="DevNull" existing="attr">
<c2>23</c2>
</b1>
</a1>
Upvotes: 3
Reputation: 24816
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- match elements with children like attrib- and copy/pass -->
<xsl:template match="*[*[starts-with(name(.),'attrib')]]">
<xsl:copy>
<xsl:apply-templates select="*[starts-with(name(.),'attrib')]"/>
<xsl:apply-templates select="@*|*[not(starts-with(name(.),'attrib'))]"/>
</xsl:copy>
</xsl:template>
<!-- match elements like attrib- and output them as attribute -->
<xsl:template match="*[starts-with(name(.),'attrib')]">
<xsl:attribute name="{substring-after(name(.),'attrib-')}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1