Reputation: 9296
I have the following xml file:
<WindowProperties>
<skin>LP Connect</skin>
<showToOperator>false</showToOperator>
<showToVisitor>false</showToVisitor>
<ChatWindow>
<field>
<key>direction</key>
<val>ltr</val>
</field>
<field>
<key>enableCustomizedHeaderImageUrl</key>
<val>false</val>
</field>
<field>
<key>brandType</key>
<val>noImage</val>
</field>
<field>
<key>brandHeight</key>
<val>158</val>
</field>
</ChatWindow>
I want to sort field in chat window using key element so the xml will look like:
<WindowProperties>
<skin>LP Connect</skin>
<showToOperator>false</showToOperator>
<showToVisitor>false</showToVisitor>
<ChatWindow>
<field>
<key>brandHeight</key>
<val>158</val>
</field>
<field>
<key>brandType</key>
<val>noImage</val>
</field>
<field>
<key>direction</key>
<val>ltr</val>
</field>
<field>
<key>enableCustomizedHeaderImageUrl</key>
<val>false</val>
</field>
</ChatWindow>
</WindowProperties>
I tried the following xsl:
<ChatWindow>
<xsl:template match="ChatWindow">
<xsl:for-each select="field">
<xsl:sort select="key"/>
<key><xsl:value-of select="key"/></key>
<value><xsl:value-of select="val"/></value>
</xsl:for-each>
</xsl:template>
</ChatWindow>
but this didn't work. Any help appreciated.
Upvotes: 0
Views: 8122
Reputation: 101682
This simple XSLT should do the job:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ChatWindow">
<xsl:copy>
<xsl:apply-templates select="field">
<xsl:sort select="key" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, this produces:
<WindowProperties>
<skin>LP Connect</skin>
<showToOperator>false</showToOperator>
<showToVisitor>false</showToVisitor>
<ChatWindow>
<field>
<key>brandHeight</key>
<val>158</val>
</field>
<field>
<key>brandType</key>
<val>noImage</val>
</field>
<field>
<key>direction</key>
<val>ltr</val>
</field>
<field>
<key>enableCustomizedHeaderImageUrl</key>
<val>false</val>
</field>
</ChatWindow>
</WindowProperties>
If you absolutely have to use <xsl:output method="text" />
for some reason, you can do the following, but I do not recommend it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="*">
<xsl:value-of select="concat('<', name(), '>')"/>
<xsl:apply-templates />
<xsl:value-of select="concat('</', name(), '>')"/>
</xsl:template>
<xsl:template match="ChatWindow">
<xsl:value-of select="concat('<', name(), '>')"/>
<xsl:apply-templates select="field">
<xsl:sort select="key" />
</xsl:apply-templates>
<xsl:value-of select="concat('</', name(), '>')"/>
</xsl:template>
</xsl:stylesheet>
Or a bit longer, but a little cleaner:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes" omit-xml-declaration="yes" />
<xsl:template name="tag">
<xsl:param name="slash" />
<xsl:value-of select="concat('<', $slash, name(), '>')"/>
</xsl:template>
<xsl:template name="cTag">
<xsl:call-template name="tag">
<xsl:with-param name="slash" select="'/'" />
</xsl:call-template>
</xsl:template>
<xsl:template match="*">
<xsl:call-template name="tag" />
<xsl:apply-templates />
<xsl:call-template name="cTag" />
</xsl:template>
<xsl:template match="ChatWindow">
<xsl:call-template name="tag" />
<xsl:apply-templates select="field">
<xsl:sort select="key" />
</xsl:apply-templates>
<xsl:call-template name="cTag" />
</xsl:template>
</xsl:stylesheet>
Upvotes: 3
Reputation: 5258
Edit: Please see answer from @JLRishe as it is the proper way of doing this. My answer below satisfies the 'text' output requirement, but in reality is more convoluted and accomplishes the same thing.
I've removed my full example and am keeping the <CDATA>
code from my original answer which I will keep here in case anyone does need to generate XML elements or other markup in a text output from an XSLT (say for generating comments or something). If you're looking to generate pure XML this is not the best way to do it:
Original Answer:
To recap so I understand the question, you have an XSLT template that returns "text" (not XML) but you want XML tags to be preserved in the output so that it looks like XML.
To do this, you need <CDATA[ ]]>
sections around the XML tags you want treated as text, such as below:
<xsl:template match="ChatWindow">
<xsl:for-each select="field">
<xsl:sort select="key"/>
<![CDATA[<key>]]>
<xsl:value-of select="key"/>
<![CDATA[</key>]]>
<![CDATA[<value>]]>
<xsl:value-of select="val"/>
<![CDATA[</value>]]>
</xsl:for-each>
</xsl:template>
Note you can also do this in the middle to combine the CDATA section but I think the above looks cleaner. (Same result)
<xsl:template match="ChatWindow">
<xsl:for-each select="field">
<xsl:sort select="key"/>
<![CDATA[<key>]]>
<xsl:value-of select="key"/>
<![CDATA[</key>
<value>]]>
<xsl:value-of select="val"/>
<![CDATA[</value>]]>
</xsl:for-each>
</xsl:template>
Upvotes: 1