HighTechTalks DotNet Forums  

IXsltContextFunction args seemingly passed wrong during Invoke

Dotnet XML microsoft.public.dotnet.xml


Discuss IXsltContextFunction args seemingly passed wrong during Invoke in the Dotnet XML forum.



Reply
 
Thread Tools Search this Thread Display Modes
  #1  
Old   
jakebbohio
 
Posts: n/a

Default IXsltContextFunction args seemingly passed wrong during Invoke - 12-05-2007 , 05:30 PM






I have a user-defined XPath function implemented using a custom
context and the IXsltContextFunction interface. For its arguments, it
accepts 3: { XPathResultType.Any, XPathResultType.Any,
XPathResultType.String }. The function is called datediff, which
converts the arguments to DateTimes, and gives args[0] - args[1] in
the units specified by args[2] (e.g., "days", "months"). Anyhow, as
an example, Let's say I have the following XML file:

<root>
<testnode DateVal="2008-12-05T00:00:00" TestName="Whatever" />
</root>

And I execute the expression: ' /root/
testnode[datediff("2008-12-25T00:00:00",@DateVal,"days") < 365]/
@TestName'
which should return the TestName attribute of any nodes whose DateVals
are within a year of 12/25/2008, give or take (for arguments sake,
let's just assume the function returns 0 if args[1] > args[0]).

The issue is that when the XPath function (datediff) Invoke is
called, and I watch the args[] array, the second argument (@DateVal),
is an XPathNodeIterator with one XPathNavigator (as it should be),
but:

The XPathNavigator is the testnode element not the DateVal attribute.
Is this the expected behavior? Why is an element node being passed in
as the argument, when, from the XPathExpression above, it's clearly
the attribute node that's being passed as an argument to the function?

By the way, I realize that I can pass "string(@EndDate)" as the second
argument, which works fine, but a function I'm building now that
returns a set representing a cross-product of node sets will need the
ability to traverse through attribute nodes.

Reply With Quote
  #2  
Old   
Oleg Tkachenko
 
Posts: n/a

Default Re: IXsltContextFunction args seemingly passed wrong during Invoke - 12-06-2007 , 04:05 PM






That's really weird. I'd like to hear the answer too.
Btw, can you provide minimal repro?

--
Oleg

jakebbohio wrote:
Quote:
I have a user-defined XPath function implemented using a custom
context and the IXsltContextFunction interface. For its arguments, it
accepts 3: { XPathResultType.Any, XPathResultType.Any,
XPathResultType.String }. The function is called datediff, which
converts the arguments to DateTimes, and gives args[0] - args[1] in
the units specified by args[2] (e.g., "days", "months"). Anyhow, as
an example, Let's say I have the following XML file:

root
testnode DateVal="2008-12-05T00:00:00" TestName="Whatever" /
/root

And I execute the expression: ' /root/
testnode[datediff("2008-12-25T00:00:00",@DateVal,"days") < 365]/
@TestName'
which should return the TestName attribute of any nodes whose DateVals
are within a year of 12/25/2008, give or take (for arguments sake,
let's just assume the function returns 0 if args[1] > args[0]).

The issue is that when the XPath function (datediff) Invoke is
called, and I watch the args[] array, the second argument (@DateVal),
is an XPathNodeIterator with one XPathNavigator (as it should be),
but:

The XPathNavigator is the testnode element not the DateVal attribute.
Is this the expected behavior? Why is an element node being passed in
as the argument, when, from the XPathExpression above, it's clearly
the attribute node that's being passed as an argument to the function?

By the way, I realize that I can pass "string(@EndDate)" as the second
argument, which works fine, but a function I'm building now that
returns a set representing a cross-product of node sets will need the
ability to traverse through attribute nodes.

Reply With Quote
  #3  
Old   
NateJacobsen
 
Posts: n/a

Default Re: IXsltContextFunction args seemingly passed wrong during Invoke - 12-07-2007 , 10:21 AM



On Dec 6, 4:05 pm, Oleg Tkachenko <f... (AT) dummy (DOT) com> wrote:
Quote:
That's really weird. I'd like to hear the answer too.
Btw, can you provide minimal repro?

--
Oleg

jakebbohio wrote:
I have a user-defined XPath function implemented using a custom
context and the IXsltContextFunction interface. For its arguments, it
accepts 3: { XPathResultType.Any, XPathResultType.Any,
XPathResultType.String }. The function is called datediff, which
converts the arguments to DateTimes, and gives args[0] - args[1] in
the units specified by args[2] (e.g., "days", "months"). Anyhow, as
an example, Let's say I have the following XML file:

root
testnode DateVal="2008-12-05T00:00:00" TestName="Whatever" /
/root

And I execute the expression: ' /root/
testnode[datediff("2008-12-25T00:00:00",@DateVal,"days") < 365]/
@TestName'
which should return the TestName attribute of any nodes whose DateVals
are within a year of 12/25/2008, give or take (for arguments sake,
let's just assume the function returns 0 if args[1] > args[0]).

The issue is that when the XPath function (datediff) Invoke is
called, and I watch the args[] array, the second argument (@DateVal),
is an XPathNodeIterator with one XPathNavigator (as it should be),
but:

The XPathNavigator is the testnode element not the DateVal attribute.
Is this the expected behavior? Why is an element node being passed in
as the argument, when, from the XPathExpression above, it's clearly
the attribute node that's being passed as an argument to the function?

By the way, I realize that I can pass "string(@EndDate)" as the second
argument, which works fine, but a function I'm building now that
returns a set representing a cross-product of node sets will need the
ability to traverse through attribute nodes.
Sure.

Here's trivial example that will output data about a node passed in:

Create a class to host the function:

public class XPathFuncTest : IXsltContextFunction
{
XPathResultType[] IXsltContextFunction.ArgTypes
{
get
{
return new XPathResultType[] { XPathResultType.Any,
XPathResultType.String };
}
}

int IXsltContextFunction.Minargs
{
get
{
return 2;
}
}

int IXsltContextFunction.Maxargs
{
get
{
return 2;
}
}

XPathResultType IXsltContextFunction.ReturnType
{
get
{
return XPathResultType.String;
}
}

object IXsltContextFunction.Invoke(XsltContext xsltContext,
object[] args, XPathNavigator docContext)
{
if (args[0] as XPathNodeIterator != null)
{
XPathNodeIterator objNI = (XPathNodeIterator)args[0];
if(objNI.Count == 0)
return "null";
if(objNI.Current == null)
objNI.MoveNext();
switch (args[1].ToString())
{
case "type":
return objNI.Current.NodeType.ToString();
break;
case "name":
return objNI.Current.Name;
break;
case "value":
default:
return objNI.Current.Value;
break;
}
}
return "";
}

}

And then a simple CustomContext implementation:

public class CustomXPathContext : XsltContext
{
private Dictionary<string, IXsltContextFunction>
objFunctionLookup = new Dictionary<string, IXsltContextFunction>();

public CustomXPathContext(NameTable objNT)
: base(objNT)
{

}

public CustomXPathContext() : this(new NameTable()) { }

public void RegisterCustomFunction(string FunctionName,
IXsltContextFunction FunctionProcessor)
{
if (!objFunctionLookup.ContainsKey(FunctionName))
objFunctionLookup.Add(FunctionName,
FunctionProcessor);
}

public NameValueCollection VariableMapping
{
get { return objVarMapping; }
set { objVarMapping = value; }
}

public override IXsltContextFunction ResolveFunction(string
prefix, string name, XPathResultType[] ArgTypes)
{
if (objFunctionLookup.ContainsKey(name))
{
return objFunctionLookup[name];
}
return null;
}

public override IXsltContextVariable ResolveVariable(string
prefix, string name)
{
return null;
}

public override bool Whitespace
{
get { return true; }
}

public override bool PreserveWhitespace(XPathNavigator node)
{
return true;
}

public override int CompareDocument(string baseUri, string
nextbaseUri)
{
return 0;
}
}

Finally, a quick and dirty console app to do the testing:

namespace TestApp
{
class Program
{
static void Main(string[] args)
{
MemoryStream ms = new MemoryStream();
StreamWriter sw = new StreamWriter(ms);
sw.Write("<root><testnode DateVal=\"2008-12-05T00:00:00\"
TestName=\"TestNameAttribute\" /></root>");
sw.Flush();
ms.Position = 0;

XPathDocument objTestDoc = new XPathDocument(ms);
XPathNavigator objNav = objTestDoc.CreateNavigator();

//Create the context, register the nodeinfo function
CustomXPathContext objContext = new CustomXPathContext();
objContext.RegisterCustomFunction("nodeinfo", new
XPathFuncTest());

//Should return "TestNameAttribute", but return an empty
string
System.Xml.XPath.XPathExpression objCompiled1 =
System.Xml.XPath.XPathExpression.Compile("string(/root/
testnode[nodeinfo(@DateVal,'type') = 'Attribute']/@TestName)");
objCompiled1.SetContext(objContext);
Console.WriteLine("@DateVal NodeType == Attribute: " +
objNav.Evaluate(objCompiled1).ToString());

//Should return an empty string, but returns
"TestNameAttribute"
System.Xml.XPath.XPathExpression objCompiled2 =
System.Xml.XPath.XPathExpression.Compile("string(/root/
testnode[nodeinfo(@DateVal,'type') = 'Element']/@TestName)");
objCompiled2.SetContext(objContext);
Console.WriteLine("@DateVal NodeType == Element: " +
objNav.Evaluate(objCompiled2).ToString());

//This returns "Root" as the node type, making me think
the the node argument passed to Invoke is identical to the context
node, for some reason:
System.Xml.XPath.XPathExpression objCompiled3 =
System.Xml.XPath.XPathExpression.Compile("nodeinfo (/root/testnode/
@DateVal,'type')");
objCompiled3.SetContext(objContext);
Console.WriteLine("@DateVal NodeType: " +
objNav.Evaluate(objCompiled3).ToString());

Console.ReadKey();
}
}
}

Just run the console app to reproduce the behavior. Maybe I'm crazy,
and it's working how it is supposed to, but the argument passed to
args[], when the argument is a nodeset, seems to always be the context
node, as opposed to the specified node.

-- Nate


Reply With Quote
Reply




Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Powered by vBulletin Version 3.5.4
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.