c# - XslCompiledTransform ignores ordering of XPathNodeIterator -


i have xslt stylesheet consumes document , outputs soap message, body in specific format defined wcf data contract (not specified here). issue wcf has peculiar notion of constitutes 'alphabetical' ordering , considers following order correct:

  • ac
  • ab

this because uses ordinal string comparison internally. details not interesting, suffice xslt <sort> doesn't natively support ordering in order transform input document format may vary, acceptable soap message, stylesheet must able order output elements according peculiar ordering. i've therefore decided implement node sorting in script block. part of c# solution , uses xslcompiledtransform , therefore msxsl:script available.

given stylesheet:

<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"                 xmlns:fn="urn:functions"     xmlns:msxsl="urn:schemas-microsoft-com:xslt"                 exclude-result-prefixes="msxsl exsl"                 xmlns:exsl="http://exslt.org/common" >   <msxsl:script implements-prefix="fn" language="c#">     <![cdata[        public class ordinalcomparer : icomparer       {           public int compare(object x, object y)           {               return string.compareordinal((string)x, (string)y);           }       }        public xpathnodeiterator ordinalsort(xpathnavigator source)       {         var query = source.compile("/*");         query.addsort(source.compile("local-name()"), new ordinalcomparer());         return source.select(query);       }         ]]>   </msxsl:script>    <xsl:template match="stuff">     <xsl:element name="body">       <xsl:element name="request">         <xsl:variable name="sort">           <xsl:apply-templates select="*"/>         </xsl:variable>         <xsl:for-each select="fn:ordinalsort($sort)">           <xsl:copy-of select="."/>         </xsl:for-each>       </xsl:element>     </xsl:element>   </xsl:template>    <xsl:output method="xml" indent="yes"/>    <xsl:template match="@* | node()">     <xsl:copy>       <xsl:apply-templates select="@* | node()"/>     </xsl:copy>   </xsl:template>  </xsl:stylesheet> 

and input document:

<?xml version='1.0' encoding='utf-8'?> <root>   <stuff>     <age></age>     <ais></ais>     <something></something>     <bmi></bmi>   </stuff> </root> 

i expect output order innermost elements follows:

  • ais
  • age
  • bmi
  • something

this not happen. instead elements emitted in order went in. debugging stylesheet executes can see ordinalsort function called, , iterator returns enumerate elements in desired order, xslt processor somehow ignores , emits elements in order encountered.

i have verified additionally parsing document in console application , running same iterator query emits elements in right order.

why, , can it? hunch have @ moment xslt engine interpreting parent navigator of iterator (which hasn't changed passed sort function) element reproduce, , ignoring contents of iterator.

i not sure how solve xpathnodeiterator or xpathnavigator, went far creating xpathnavigator[] xpathnodeiterator, avoid lazy evaluation effects, somehow ended same result you.

so alternative have written code using dom implementation in .net framework create new nodes in right sort order:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"                 xmlns:mf="urn:functions"     xmlns:msxsl="urn:schemas-microsoft-com:xslt"                 exclude-result-prefixes="msxsl exsl mf"                 xmlns:exsl="http://exslt.org/common" >   <msxsl:script implements-prefix="mf" language="c#">     <![cdata[        public class ordinalcomparer : icomparer       {           public int compare(object x, object y)           {               return string.compareordinal((string)x, (string)y);           }       }        public xpathnavigator ordinalsort(xpathnavigator source)       {         var query = source.compile("/root/*");         query.addsort("local-name()", new ordinalcomparer());         xpathnodeiterator result = source.select(query);         xmldocument resultdoc = new xmldocument();         xmldocumentfragment frag = resultdoc.createdocumentfragment();         foreach (xpathnavigator item in result)         {           frag.appendchild(resultdoc.readnode(item.readsubtree()));         }         return frag.createnavigator();       }         ]]>   </msxsl:script>    <xsl:output method="xml" indent="yes"/>    <xsl:template match="@* | node()">     <xsl:copy>       <xsl:apply-templates select="@* | node()"/>     </xsl:copy>   </xsl:template>    <xsl:template match="stuff">     <body>       <request>         <xsl:variable name="sort-rtf">           <root>             <xsl:copy-of select="*"/>           </root>         </xsl:variable>         <xsl:variable name="sort" select="exsl:node-set($sort-rtf)"/>         <xsl:variable name="sorted" select="mf:ordinalsort($sort)"/>         <xsl:copy-of select="$sorted"/>       </request>     </body>   </xsl:template>  </xsl:stylesheet> 

using approach result is

<root>   <body><request><ais /><age /><bmi /><something /></request></body> </root> 

i have streamlined code to

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"                 xmlns:mf="urn:functions"     xmlns:msxsl="urn:schemas-microsoft-com:xslt"                 exclude-result-prefixes="msxsl exsl mf"                 xmlns:exsl="http://exslt.org/common" >   <msxsl:script implements-prefix="mf" language="c#">     <![cdata[        public class ordinalcomparer : icomparer       {           public int compare(object x, object y)           {               return string.compareordinal((string)x, (string)y);           }       }        public xpathnavigator ordinalsort(xpathnavigator source)       {         var query = source.compile("*");         query.addsort("local-name()", new ordinalcomparer());         xpathnodeiterator result = source.select(query);         xmldocument resultdoc = new xmldocument();         xmldocumentfragment frag = resultdoc.createdocumentfragment();         foreach (xpathnavigator item in result)         {           frag.appendchild(resultdoc.readnode(item.readsubtree()));         }         return frag.createnavigator();       }         ]]>   </msxsl:script>    <xsl:output method="xml" indent="yes"/>    <xsl:template match="@* | node()">     <xsl:copy>       <xsl:apply-templates select="@* | node()"/>     </xsl:copy>   </xsl:template>    <xsl:template match="stuff">     <body>       <request>         <xsl:variable name="sorted" select="mf:ordinalsort(.)"/>         <xsl:copy-of select="$sorted"/>       </request>     </body>   </xsl:template>  </xsl:stylesheet> 

having construct xmlnodes in extension function c# "script" seems overhead not sure how solve otherwise.


Comments

Popular posts from this blog

c++ - Delete matches in OpenCV (Keypoints and descriptors) -

java - Could not locate OpenAL library -

sorting - opencl Bitonic sort with 64 bits keys -