I stumbled upon this while developing a custom XPathNavigator.
It appears that copy action for attributes is broken in the .net
framework XSLT processor.
The intent was to just copy the entities and attributes from the
source XML into the output using simple "identity" like XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/ | @* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This appears to loose the attributes of non-leaf entities when run
over a custom XPathNavigator. The problem appears to be related to the
order of processing. When run over a custom XPathNavigator the child
entities are processed before the attributes in the above XSLT.
The situation can be recreated over a regular XmlDocument using
following XSLT:
This does not work (notice the order of processing - attributes are
processed first):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*|/">
<xsl:copy>
<xsl:apply-templates select="*" />
<xsl:apply-templates select="@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
This does work (notice the order of processing - attributes are
processed first)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*|/">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="*" />
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
Here is a quick and dirty code snippet:
---------------------------------------------------------
String xsltFileName = "test.xslt";
XmlReader transfromReader = new XmlTextReader(xsltFileName);
XslTransform transform = new XslTransform();
transform.Load(transfromReader);
StringBuilder result = new StringBuilder();
TextWriter writer = new StringWriter(result);
String xmltestFileName = "test.xml";
XmlDocument testDocument = new XmlDocument();
testDocument.Load(xmltestFileName);
transform.Transform(testDocument,null,writer);
---------------------------------------------------------
Any XML document with several levels of entity nesting and some
attributes at each level can be used to recreate the situation.