Reputation: 24518
I am trying to do a XSLT transformation where the input XML is arbitrary. The only thing that's constant in it is that it'll have a node the name of which starts with 'first'. I need to get the value of that node and it's immediate sibling. However, the following template produces only the XML declaration.
If it matters, this code is in Ruby using Nokogiri XML parser. However, I think this is more of a XSLT/XPath question rather than a Ruby question and hence tagging accordingly.
Input XML:
<?xml version="1.0"?>
<employees>
<employee>
<first_name>Winnie</first_name>
<last_name>the Pooh</last_name>
</employee>
<employee>
<first_name>Jamie</first_name>
<last_name>the Weeh</last_name>
</employee>
</employees>
Desired output XML:
<?xml version="1.0"?>
<people>
<person>
<first>Winnie</first>
<last>the Pooh</last>
</person>
<person>
<first>Jamie</first>
<last>the Weeh</last>
</person>
</people>
XSLT:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8" />
<xsl:template match="/">
<xsl:for-each select="node()[starts-with(name(), 'first')]">
<people>
<person>
<name>
<first><xsl:value-of select="." /></first>
<last><xsl:value-of select="following-sibling::*[1]" /></last>
</name>
</person>
</people>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Upvotes: 3
Views: 21175
Reputation: 24518
<?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" indent="yes" encoding="UTF-8" />
<!-- match the node that has a name starting with 'first' -->
<xsl:template match="node()[starts-with(name(), 'first')]">
<xsl:element name="people">
<xsl:element name="person">
<xsl:element name="name">
<xsl:element name="first">
<xsl:value-of select="." />
</xsl:element>
<xsl:element name="last">
<xsl:value-of select="following-sibling::*[1]" />
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
<xsl:apply-templates />
</xsl:template>
<!-- stop the processor walking the rest of the tree and hitting text nodes -->
<xsl:template match="text()|@*" />
</xsl:stylesheet>
Upvotes: 4
Reputation: 163302
I don't see how you can change employees/employee to persons/person if the input XML is as arbitrary as you suggest. However, you can achieve roughly the right effect with
<xsl:template match="*">
<xsl:copy><xsl:apply-templates/></xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'first')]">
<first><xsl:apply-templates/></first>
</xsl:template>
<xsl:template match="*[preceding-sibling::*[1][starts-with(name(), 'first')]]">
<last><xsl:apply-templates/></last>
</xsl:template>
Upvotes: 4