0

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.

7
  • 1
    The accepted answer from this question should help you - stackoverflow.com/questions/9824703/… Commented Mar 10, 2013 at 15:23
  • This returns plain text not xml Commented Mar 10, 2013 at 15:27
  • The output of the XSLT should be irrelevant - you're sorting the input and then do what you need with it. The gist of the answer in that question is that you need to match on field/key instead of just key, I think Commented Mar 10, 2013 at 15:33
  • could you write that as xsl as I'm totally new to xslt? Commented Mar 10, 2013 at 15:36
  • 1
    What do you mean by "This returns plain text not xml"? You seem to clearly be looking to produce XML. Commented Mar 10, 2013 at 17:42

2 Answers 2

3

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('&lt;', name(), '&gt;')"/>
    <xsl:apply-templates />
    <xsl:value-of select="concat('&lt;/', name(), '&gt;')"/>
  </xsl:template>

  <xsl:template match="ChatWindow">
    <xsl:value-of select="concat('&lt;', name(), '&gt;')"/>
    <xsl:apply-templates select="field">
      <xsl:sort select="key" />
    </xsl:apply-templates>
    <xsl:value-of select="concat('&lt;/', name(), '&gt;')"/>
  </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('&lt;', $slash, name(), '&gt;')"/>
  </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>
Sign up to request clarification or add additional context in comments.

9 Comments

OP says his output is text though, and when I run your (awesome) template with method output="text", it does not produce the xml tags as OP wants. I was about to tell OP to forget my answer, too!
However +1 for a cleaner way of doing it. I was trying to do something with <tag-of> and name() but it isn't working for me. In text output with your template, that would be the perfect solution.
@DavidKhaykin, <xsl:output method="text"> is for producing text -- not markup. You have misunderstood the OP.
@DavidKhaykin I saw OP's comment about plain text, but his question seems to clearly indicate that he wants XML as the result. I think he is confused. In your XSLT, you've simply created a roundabout way of producing XML with the output set to text, but I can't think of any situation where that would be worthwhile.
@* selects all attributes of the current node. node() selects all node children of the current node. | combines the results of multiple selections.
|
1

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>

3 Comments

Could you write the full xsl file to generate all the xml elements?
@David In case you are interested, I have added two concise yet complete solutions for accomplishing this with mode="text".
@JLRishe - yes this is great, saving for later. I was trying to build that example and for some reason wasn't working for me with the name() call. Will test yours out. This question (interpreting the need) and your answer have taught me something today - thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.