Reputation: 178
I'm trying to convert an xml that has a root element and one level deep of child elements. These elements can have the same attribute name. What I am looking for is a way to transform the xml in such a way that the related attributes are nested correctly.
The xml is generated by an HTML form submission (I have control over the form field names).
The resulting XML is generated:
<root>
<project_id>1</project_id>
<project_name>Project 1</project_name>
<project_id>2</project_id>
<project_name>Project 2</project_name>
<project_id>3</project_id>
<project_name>Project 3</project_name>
</root>
<root>
<project>
<id>1</id>
<name>Project 1</name>
</project>
<project>
<id>2</id>
<name>Project 2</name>
</project>
<project>
<id>3</id>
<name>Project 3</name>
</project>
<root>
Note: I prepended 'r_' to the repeated attributes. <r_project_id> 2</r_project_id>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="node()|@*">
<project>
<xsl:apply-templates select="*[matches(name(), '^project_')]"/>
</project>
<project>
<xsl:apply-templates select="*[matches(name(), '^r_project')]"/>
</project>
</xsl:template>
<xsl:template match="*[matches(name(), '^r_project_')]">
<xsl:apply-templates select="*[matches(name(), '^r_project_')]"/>
<xsl:copy-of select="*"/>
</xsl:template>
<xsl:template match="*[matches(name(), '^project_')]">
<xsl:element name="{replace(name(), '^project_', '')}">
<xsl:copy-of select="*"/>
</xsl:element>
</xsl:template>
<xsl:template match="*[matches(name(), '^r_project_')]">
<xsl:element name="{replace(name(), '^r_project_', '')}">
<xsl:copy-of select="*"/>
</xsl:element>
</xsl:template>
<root>
<project>
<id></id>
<name></name>
</project>
<project>
<id></id>
<name></name>
<id></id>
<name></name>
</project>
</root>
Is there a simpler method to creating unique XML elements without having to create a extremely verbose xslt transformation that captures all possible repeated elements?
Upvotes: 1
Views: 127
Reputation: 163549
The solution from michael.hor257k looks fine, but a slightly more idiomatic and flexible solution in XSLT 2.0 might be
<xsl:template match="/">
<root>
<xsl:for-each-group select="root/*" group-starting-with="project_id">
<project>
<xsl:apply-templates select="current-group()" mode="rename"/>
</project>
</xsl:for-each>
</root>
</xsl:template>
<xsl:template match="*" mode="rename">
<xsl:element name="{substring-after(name(), 'project_')}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
Upvotes: 2
Reputation: 117102
I can't follow the logic of your XSLT. Is there a reason why this couldn't be simply:
<xsl:template match="/">
<root>
<xsl:for-each select="root/project_id">
<project>
<id><xsl:value-of select="."/></id>
<name><xsl:value-of select="following-sibling::project_name"/></name>
</project>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1