Showing posts with label XSL. Show all posts
Showing posts with label XSL. Show all posts

Monday, 18 February 2008

Re-arrange the order of elements in an XML document

Often people need to reorganize their XML for further processing, and this is a great opportunity to clarify available techniques.

You can use xsl:copy and/or copy-of, although I doubt your goal in practice will always be as simple as that.


Input xml:
<ROOT>
<A1>A</A1>
<B1>B</B1>
<C1>C</C1>
<D1>D</D1>
</ROOT>

Required output xml:
<ROOT>
<A1>A</A1>
<D1>D</D1>
<B1>B</B1>
<C1>C</C1>
</ROOT>


If you need to restructure the children of the root element for example, and you're confident about the simplicity of the XML you're trying to restructure, then you'd do something like this:
<xsl:template match="/ROOT">
<xsl:copy>
<xsl:copy-of select="A1"/>
<xsl:copy-of select="D1"/>
<xsl:copy-of select="B1"/>
<xsl:copy-of select="C1"/>
</xsl:copy>
</xsl:template>

The above code will output what we've planned for. Now, if you have a much larger document, and what you're really after is moving some of the nodes around while maintaining an abstract hierarchy, you could use the technique in example code below.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:template match="*">
<xsl:apply-templates select="self::*" mode="copy"/>
</xsl:template>

<xsl:template match="A1">
<xsl:apply-templates select="self::*" mode="copy"/>
<xsl:apply-templates select="../D1" mode="copy"/>
</xsl:template>


<xsl:template match="B1">
<xsl:apply-templates select="self::*" mode="copy"/>
<xsl:apply-templates select="../C1" mode="copy"/>
</xsl:template>

<xsl:template match="D1"/>
<xsl:template match="C1"/>

<xsl:template match="*" mode="copy">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>

<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>


In this case you are copying an abstract document structure and repositioning some of the elements in strategic places.

Tuesday, 12 February 2008

XPath expressions special characters in XSL

There is a common misunderstanding about the use of special characters by Xpath expressions in XSL and Xpath expressions in other languages, I'll try to help clarify some of the myths in this blog.

If you were using Java or C#, the actual expression would be either passed as a string parameter to a constructor method or any other method, or even just stored in a string variable to then be used as a parameter to one of those methods. Simple. Although, if you'd consider the following example, you'd have to escape the double quote characters with a back slash character.

String xPathExpr = "/root/my/node[@attribute='\"this & that value in quotes\"']";

In XML you have similar constraints, if you try to put a double quote character inside the value of an attribute, the XML parser thinks the value of the attribute ends on the first occurrence of the double quote character, therefore the document won't be a valid XML document. Unless of coarse you replace the character with the equivalent character reference &#34;.

Because XSL documents are XML documents, some of these rules need to be taken into account. By default most XSLT Procesors allocate entities to at least 3 of those character references:
<!ENTITY amp CDATA "&#38;" -- ampersand, U+0026 ISOnum -->
<!ENTITY lt CDATA "&#60;" -- less-than sign, U+003C ISOnum -->
<!ENTITY gt CDATA "&#62;" -- greater-than sign, U+003E ISOnum -->

Meaning you can use those three by preceding the entity name with an "&" character and terminate the entity refference with an ";" character.
&amp; = &
&lt; = <
&gt; = >

So if you were to use the earlier XPath expression in XSL it'd look like this instead:

<xsl:variable name="myNode" select="/root/my/node[@attribute='&#34;this &amp; that value in quotes&#34;'"/>
or
<xsl:variable name="myNode" select="/root/my/node[@attribute='&#34;this &#38; that value in quotes&#34;'"/>

The characters > and < are often used in XPath expressions that evaluate numerical values.

<xsl:variable name="isMyOtherAttributeGreater" select="/root/my/node/@attribute &gt; /root/my/other/node/@otherAttribute"/>
This expression returns a Boolean value, of true when the value of @attribute of the first XPath expression is greater than value of the @otherAttribute in the second expression.

You could also combine < and > with the "=" character to construct the greater than or equal and the less than or equal evaluation expressions.
@attribute &gt;= @otherAttribute
@attribute &lt;= @otherAttribute

<xsl:variable name="myNode" select="/root/my/node[@attribute &gt;= ../node[1]/@otherAttribute]"/>


see following links for further info on entities and character encoding in XML:
http://www.xml.com/pub/a/98/08/xmlqna0.html
http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
http://www.w3.org/TR/html401/sgml/entities.html