Reputation: 32086
I have this source XML from a vendor:
<?xml version="1.0" encoding="UTF-8"?>
<src:view name="books"
xmlns="xyz.com/wow"
xmlns:src="xyz.com/fun"
xmlns:xsi="w3.org/2001/XMLSchema-instance">
<books>
<title>Searching for Answers</title>
</books>
<books>
<title>Get Rich Quick</title>
</books>
<books>
<title>Negotiating with the Grim Reaper</title>
</books>
</src:view>
I want to transform into a new document with root element books
, then copy all books
elements but change the name from books
to books_record
. Documents will be arbitrary,(books today, widgets tomorrow, etc), but the element name for the root's children is always available from /@name
, that's the vendors convention.
<books>
<books_record>
<title>Searching for Answers</title>
</books_record>
<books_record>
<title>Get Rich Quick</title>
</books_record>
<books_record>
<title>Negotiating with the Grim Reaper</title>
</books_record>
</books>
I managed the get right the root element and copying of all child elements, but I'm not sure how to change all <books>
to <books_record>
. Also, is what I have so far efficient and valid? I know it works, just not 100% sure if it's the best approach.
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="xsl" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="@*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="/*">
<xsl:variable name="rootElementName">
<xsl:value-of select="@name" />
</xsl:variable>
<xsl:element name="{$rootElementName}">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="/*/node()">
<xsl:variable name="rootElementName">
<xsl:value-of select="/*/@name" />
</xsl:variable>
<xsl:element name="{$rootElementName}_record">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:transform>
Upvotes: 0
Views: 2857
Reputation: 117140
I believe you could simplify this to:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="@*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="/*">
<books>
<xsl:apply-templates />
</books>
</xsl:template>
<xsl:template match="*[local-name()='books']">
<books_record>
<xsl:apply-templates />
</books_record>
</xsl:template>
</xsl:transform>
Of course, if you know the expected format of the input, you can get more explicit - thus both shorter and more efficient, for example:
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:src="xyz.com/fun"
xmlns:wow="xyz.com/wow"
exclude-result-prefixes="src wow">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<books>
<xsl:for-each select="src:view/wow:books">
<books_record>
<title>
<xsl:value-of select="wow:title"/>
</title>
</books_record>
</xsl:for-each>
</books>
</xsl:template>
</xsl:transform>
If I understand correctly what you mean by "generic", then:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="tableName" select="/*/@name" />
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="@*" />
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="{$tableName}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*/*">
<xsl:element name="{$tableName}_record">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:transform>
Note:
exclude-result-prefixes="xsl"
Upvotes: 2