An interesting question has been raised in microsoft.public.dotnet.xml newsgroup: how to compile XPath expression without a XML document at hands? XPathNavigator class does provide such functionality via Compile() method, but XPathNavigator is abstract class hence this functionality is available only to its implementers, such as internal DocumentXPathNavigator and XPathDocumentNavigator classes, which are accessible only via corresponding XmlDocument and XPathDocument.
Therefore obvious solutions are: using dummy XmlDocument or XPathDocument object to get XPathNavigator and make use of its Compile() method or implement dummy XPathNavigator class. Dummy object vs dummy implementation, hehe. Well, dummy implementation at least doesn't allocate memory, so I'm advocating this solution. Below is the implementation and its usage:
public sealed class XPathCompiler { private sealed class DummyXpathNavigator : XPathNavigator { public override XPathNavigator Clone() { return new DummyXpathNavigator(); } public override XPathNodeType NodeType { get { return XPathNodeType.Root; } } public override string LocalName { get { return String.Empty; } } public override string NamespaceURI { get { return String.Empty; } } public override string Name { get { return String.Empty; } } public override string Prefix { get { return String.Empty; } } public override string Value { get { return String.Empty; } } public override string BaseURI { get { return String.Empty; } } public override String XmlLang { get { return String.Empty; } } public override bool IsEmptyElement { get { return false; } } public override XmlNameTable NameTable { get { return null; } } public override bool HasAttributes { get { return false; } } public override string GetAttribute(string localName, string namespaceURI) { return string.Empty; } public override bool MoveToAttribute(string localName, string namespaceURI) { return false; } public override bool MoveToFirstAttribute() { return false; } public override bool MoveToNextAttribute() { return false; } public override string GetNamespace(string name) { return string.Empty; } public override bool MoveToNamespace(string name) { return false; } public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) { return false; } public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) { return false; } public override bool HasChildren { get { return false; } } public override bool MoveToNext() { return false; } public override bool MoveToPrevious() { return false; } public override bool MoveToFirst() { return false; } public override bool MoveToFirstChild() { return false; } public override bool MoveToParent() { return false; } public override void MoveToRoot() {} public override bool MoveTo( XPathNavigator other ) { return false; } public override bool MoveToId(string id) { return false; } public override bool IsSamePosition(XPathNavigator other) { return false; } public override XPathNodeIterator SelectDescendants(string name, string namespaceURI, bool matchSelf) { return null; } public override XPathNodeIterator SelectChildren(string name, string namespaceURI) { return null; } public override XPathNodeIterator SelectChildren(XPathNodeType nodeType) { return null; } public override XmlNodeOrder ComparePosition(XPathNavigator navigator) { return new XmlNodeOrder(); } } private static XPathNavigator _nav = new DummyXpathNavigator(); public static XPathExpression Compile(string xpath) { return _nav.Compile(xpath); } } public class XPathCompilerTest { static void Main(string[] args) { //Document-free compilation XPathExpression xe = XPathCompiler.Compile("/foo"); //Usage of the compiled expression XPathDocument doc = new XPathDocument(new StringReader("<foo/>")); XPathNavigator nav = doc.CreateNavigator(); XPathNodeIterator ni = nav.Select(xe); while (ni.MoveNext()) { Console.WriteLine(ni.Current.Name); } } }
Vlad, always call MoveNext before accessing XPathNodeIterator.
How write a node in xsl using C#
next code is not working.
Thanks,
Vlad
<msxsl:script language="JavaScript" implements-prefix="page-script">
<![CDATA[
function dotNet(nd){
try{
if(nd.Current)return true;
return false;
}catch(e){
return false;
}
}
function fragmentToText(nd){
try{
if(dotNet(nd)){
nd.MoveNext();
return nd.Current.Value;
}else{
return nd.item(0).xml;
}
}catch(e){
return "Error in function fragmentToText(nd) xsl:template name='add-required-xml-data-islands' ::" +e.message;
}
}
]]>
Well, may be you are right. May be I'm too performance concerned ;)
Actually you forgot about fields and static stuff. According to CLR profiler, running the following program:
static void Main(string[] args) {
XmlDocument doc = new XmlDocument();
XPathNavigator nav = doc.CreateNavigator();
}
allocates 79 Kb:
: 79 kB (100.00%)
Test::Main static void (String[]): 70 kB (89.24%)
System.AppDomain::SetupDomain void (System.LoaderOptimization): 2.2 kB (2.74%)
System.Xml.XmlNode::CreateNavigator System.Xml.XPath.XPathNavigator (): 68 kB (86.22%)
System.Xml.XmlDocument::.ctor void (): 2.3 kB (2.91%)
System.AppDomain::SetupFusionStore void (System.AppDomainSetup): 2.1 kB (2.64%)
System.Xml.XmlDocument::CreateNavigator System.Xml.XPath.XPathNavigator (System.Xml.XmlNode): 68 kB (86.22%)
System.Xml.XmlDocument::.ctor void (System.Xml.XmlImplementation): 1.3 kB (1.60%)
System.Xml.DocumentXPathNavigator::.ctor void (System.Xml.XmlNode): 68 kB (86.07%)
System.Xml.XmlDocument::CreateAttribute System.Xml.XmlAttribute (String String String): 68 kB (85.54%)
System.Xml.XmlAttribute::.ctor void (System.Xml.XmlName System.Xml.XmlDocument): 67 kB (84.81%)
System.Xml.XmlDocument::CheckName static void (String): 67 kB (84.75%)
System.Xml.XmlCharType::.cctor static void (): 64 kB (81.09%)
System.Byte [] : 64 kB (81.09%)
System.String : 6.6 kB (8.34%)
System.Object [] : 6.0 kB (7.62%)
The bigger memory hog here is in XmlCharType class, which preallocates static 65536 bytes array for all allowed in XML characters. Actually as it's static it might be allocated already, so empty XmlDocument is really cheap in memory terms.
Anyway, just as a matter of nitpicking I don't like any dummy objects, even cheap ones.
Well, if you actually look at the XmlDocument ctor, the allocation sequence is:
- new XmlImplementation()
- new NameTable()
next, its CreateNavigator() method, only creates a DocumentXPathNavigator() and stores a couple references (nothing "newed", except an XmlAttribute.
That makes up only 3 extra objects, which are pretty much empty, so I really don't see the big issue here.