ユーザー定義の関数と変数
XPathNavigator クラスは、XPathDocument データの操作に使用されるメソッドのセットを提供します。 XPath クエリ式で使用する拡張関数および変数を実装することで、標準の XPath 関数を補完できます。 SetContext メソッドは、XsltContext から派生した、ユーザー定義のコンテキストを受け取ることができます。 ユーザー定義の関数は、カスタム コンテキストで解決されます。
拡張関数および変数は、XML インジェクション攻撃を防止するのに役立ちます。 このようなシナリオでは、処理命令と連結された生の入力としてではなく、ユーザー入力がカスタム変数に割り当てられ、拡張関数で処理されます。 拡張関数および変数にユーザー入力が含まれて、設計者が意図したように、XML データに対してだけ機能します。
拡張関数および変数を使用するには、カスタム クラス XsltContext を、拡張関数および変数をサポートする IXsltContextFunction インターフェイスおよび IXsltContextVariable インターフェイスと共に実装します。 XPathExpression は、ユーザー入力をその XsltArgumentList と共にカスタム XsltContext に追加します。
XPathExpression は、式で識別されるノードを見つけて処理するのに XPathNavigator が使用する、コンパイル済みクエリを表します。
XsltContext から派生したカスタム コンテキスト クラスの実装を次の例に示します。 コード コメントは、クラスのメンバーと、カスタム関数でのそれらのメンバーの使用法を説明しています。 このコードの後に、関数および変数の実装と、それらの実装を使用するサンプル アプリケーションがあります。
class CustomContext : System.Xml.Xsl.XsltContext
{
private const string ExtensionsNamespaceUri = "http://xpathExtensions";
// XsltArgumentList to store names and values of user-defined variables.
private XsltArgumentList argList;
public CustomContext()
{
}
public CustomContext(NameTable nt, XsltArgumentList args)
: base(nt)
{
argList = args;
}
// Function to resolve references to user-defined XPath extension
// functions in XPath query expressions evaluated by using an
// instance of this class as the XsltContext.
public override System.Xml.Xsl.IXsltContextFunction ResolveFunction(
string prefix, string name,
System.Xml.XPath.XPathResultType[] argTypes)
{
// Verify namespace of function.
if (this.LookupNamespace(prefix) == ExtensionsNamespaceUri)
{
string strCase = name;
switch (strCase)
{
case "CountChar":
return new XPathExtensionFunctions(2, 2, XPathResultType.Number,
argTypes, "CountChar");
case "FindTaskBy": // This function is implemented but not called.
return new XPathExtensionFunctions(2, 2, XPathResultType.String,
argTypes, "FindTaskBy");
case "Right": // This function is implemented but not called.
return new XPathExtensionFunctions(2, 2, XPathResultType.String,
argTypes, "Right");
case "Left": // This function is implemented but not called.
return new XPathExtensionFunctions(2, 2, XPathResultType.String,
argTypes, "Left");
}
}
// Return null if none of the functions match name.
return null;
}
// Function to resolve references to user-defined XPath
// extension variables in XPath query.
public override System.Xml.Xsl.IXsltContextVariable ResolveVariable(
string prefix, string name)
{
if (this.LookupNamespace(prefix) == ExtensionsNamespaceUri || !prefix.Equals(string.Empty))
{
throw new XPathException(string.Format("Variable '{0}:{1}' is not defined.", prefix, name));
}
// Verify name of function is defined.
if (name.Equals("text") || name.Equals("charToCount") ||
name.Equals("right") || name.Equals("left"))
{
// Create an instance of an XPathExtensionVariable
// (custom IXsltContextVariable implementation) object
// by supplying the name of the user-defined variable to resolve.
XPathExtensionVariable var;
var = new XPathExtensionVariable(prefix, name);
// The Evaluate method of the returned object will be used at run time
// to resolve the user-defined variable that is referenced in the XPath
// query expression.
return var;
}
return null;
}
// Empty implementation, returns false.
public override bool PreserveWhitespace(System.Xml.XPath.XPathNavigator node)
{
return false;
}
// empty implementation, returns 0.
public override int CompareDocument(string baseUri, string nextbaseUri)
{
return 0;
}
public override bool Whitespace
{
get
{
return true;
}
}
// The XsltArgumentList property is accessed by the Evaluate method of the
// XPathExtensionVariable object that the ResolveVariable method returns. It is used
// to resolve references to user-defined variables in XPath query expressions.
public XsltArgumentList ArgList
{
get
{
return argList;
}
}
}
Class CustomContext
Inherits XsltContext
Private Const ExtensionsNamespaceUri As String = "http://xpathExtensions"
' XsltArgumentList to store names and values of user-defined variables.
Private m_ArgList As XsltArgumentList
Public Sub New()
End Sub
Public Sub New(ByVal NT As NameTable, ByVal Args As XsltArgumentList)
MyBase.New(NT)
m_ArgList = Args
End Sub
' Empty implementation, returns 0.
Public Overrides Function CompareDocument(ByVal BaseUri As String, ByVal NextBaseUri As String) As Integer
Return 0
End Function
' Empty implementation, returns false.
Public Overrides Function PreserveWhitespace(ByVal Node As XPathNavigator) As Boolean
Return False
End Function
Public Overrides Function ResolveFunction(ByVal Prefix As String, ByVal Name As String, ByVal ArgTypes() As XPathResultType) As IXsltContextFunction
If LookupNamespace(Prefix) = ExtensionsNamespaceUri Then
Select Case Name
Case "CountChar"
Return New XPathExtensionFunctions(2, 2, _
XPathResultType.Number, ArgTypes, "CountChar")
Case "FindTaskBy" ' Implemented but not called.
Return New XPathExtensionFunctions(2, 2, _
XPathResultType.String, ArgTypes, "FindTaskBy")
Case "Right" ' Implemented but not called.
Return New XPathExtensionFunctions(2, 2, _
XPathResultType.String, ArgTypes, "Right")
Case "Left" ' Implemented but not called.
Return New XPathExtensionFunctions(2, 2, _
XPathResultType.String, ArgTypes, "Left")
Case Else
End Select
End If
' Return Nothing if none of the functions match name.
Return Nothing
End Function
' Function to resolve references to user-defined XPath
' extension variables in XPath query.
Public Overrides Function ResolveVariable(ByVal Prefix As String, ByVal Name As String) As IXsltContextVariable
If LookupNamespace(Prefix) = ExtensionsNamespaceUri OrElse Len(Prefix) > 0 Then
Throw New XPathException(String.Format("Variable '{0}:{1}' is not defined.", Prefix, Name))
End If
Select Case Name
Case "charToCount", "left", "right", "text"
' Create an instance of an XPathExtensionVariable
' (custom IXsltContextVariable implementation) object
' by supplying the name of the user-defined variable to resolve.
Return New XPathExtensionVariable(Prefix, Name)
' The Evaluate method of the returned object will be used at run time
' to resolve the user-defined variable that is referenced in the XPath
' query expression.
Case Else
End Select
' Return Nothing if none of the variables match name.
Return Nothing
End Function
Public Overrides ReadOnly Property Whitespace() As Boolean
Get
Return True
End Get
End Property
' The XsltArgumentList property is accessed by the Evaluate method of the
' XPathExtensionVariable object that the ResolveVariable method returns.
' It is used to resolve references to user-defined variables in XPath query
' expressions.
Public ReadOnly Property ArgList() As XsltArgumentList
Get
Return m_ArgList
End Get
End Property
End Class
次のコードでは IXsltContextFunction を実装します。 IXsltContextFunction を実装するクラスは、ユーザー定義の関数を解決し、実行します。 この例では、宣言 private int CountChar(string title, char charToCount)
で識別される関数を使用します。
コード コメントでは、クラスのメンバーを示しています。
// The interface that resolves and executes a specified user-defined function.
public class XPathExtensionFunctions : System.Xml.Xsl.IXsltContextFunction
{
// The data types of the arguments passed to XPath extension function.
private System.Xml.XPath.XPathResultType[] argTypes;
// The minimum number of arguments that can be passed to function.
private int minArgs;
// The maximum number of arguments that can be passed to function.
private int maxArgs;
// The data type returned by extension function.
private System.Xml.XPath.XPathResultType returnType;
// The name of the extension function.
private string FunctionName;
// Constructor used in the ResolveFunction method of the custom XsltContext
// class to return an instance of IXsltContextFunction at run time.
public XPathExtensionFunctions(int minArgs, int maxArgs,
XPathResultType returnType, XPathResultType[] argTypes, string functionName)
{
this.minArgs = minArgs;
this.maxArgs = maxArgs;
this.returnType = returnType;
this.argTypes = argTypes;
this.FunctionName = functionName;
}
// Readonly property methods to access private fields.
public System.Xml.XPath.XPathResultType[] ArgTypes
{
get
{
return argTypes;
}
}
public int Maxargs
{
get
{
return maxArgs;
}
}
public int Minargs
{
get
{
return maxArgs;
}
}
public System.Xml.XPath.XPathResultType ReturnType
{
get
{
return returnType;
}
}
// XPath extension functions.
private int CountChar(XPathNodeIterator node, char charToCount)
{
int charCount = 0;
for (int charIdx = 0; charIdx < node.Current.Value.Length; charIdx++)
{
if (node.Current.Value[charIdx] == charToCount)
{
charCount++;
}
}
return charCount;
}
// This overload will not force the user
// to cast to string in the xpath expression
private string FindTaskBy(XPathNodeIterator node, string text)
{
if (node.Current.Value.Contains(text))
return node.Current.Value;
else
return "";
}
private string Left(string str, int length)
{
return str.Substring(0, length);
}
private string Right(string str, int length)
{
return str.Substring((str.Length - length), length);
}
// Function to execute a specified user-defined XPath extension
// function at run time.
public object Invoke(System.Xml.Xsl.XsltContext xsltContext,
object[] args, System.Xml.XPath.XPathNavigator docContext)
{
if (FunctionName == "CountChar")
return (Object)CountChar((XPathNodeIterator)args[0],
Convert.ToChar(args[1]));
if (FunctionName == "FindTaskBy")
return FindTaskBy((XPathNodeIterator)args[0],
Convert.ToString(args[1]));
if (FunctionName == "Left")
return (Object)Left(Convert.ToString(args[0]),
Convert.ToInt16(args[1]));
if (FunctionName == "Right")
return (Object)Right(Convert.ToString(args[0]),
Convert.ToInt16(args[1]));
return null;
}
}
' The interface that resolves and executes a specified user-defined function.
Public Class XPathExtensionFunctions
Implements IXsltContextFunction
' The data types of the arguments passed to XPath extension function.
Private m_ArgTypes() As XPathResultType
' The minimum number of arguments that can be passed to function.
Private m_MinArgs As Integer
' The maximum number of arguments that can be passed to function.
Private m_MaxArgs As Integer
' The data type returned by extension function.
Private m_ReturnType As XPathResultType
' The name of the extension function.
Private m_FunctionName As String
' Constructor used in the ResolveFunction method of the custom XsltContext
' class to return an instance of IXsltContextFunction at run time.
Public Sub New(ByVal MinArgs As Integer, ByVal MaxArgs As Integer, ByVal ReturnType As XPathResultType, ByVal ArgTypes() As XPathResultType, ByVal FunctionName As String)
m_MinArgs = MinArgs
m_MaxArgs = MaxArgs
m_ReturnType = ReturnType
m_ArgTypes = ArgTypes
m_FunctionName = FunctionName
End Sub
' Readonly property methods to access private fields.
Public ReadOnly Property ArgTypes() As XPathResultType() Implements IXsltContextFunction.ArgTypes
Get
Return m_ArgTypes
End Get
End Property
Public ReadOnly Property MaxArgs() As Integer Implements IXsltContextFunction.Maxargs
Get
Return m_MaxArgs
End Get
End Property
Public ReadOnly Property MinArgs() As Integer Implements IXsltContextFunction.Minargs
Get
Return m_MinArgs
End Get
End Property
Public ReadOnly Property ReturnType() As XPathResultType Implements IXsltContextFunction.ReturnType
Get
Return m_ReturnType
End Get
End Property
' Function to execute a specified user-defined XPath
' extension function at run time.
Public Function Invoke(ByVal Context As XsltContext, ByVal Args() As Object, ByVal DocContext As XPathNavigator) As Object Implements IXsltContextFunction.Invoke
Select Case m_FunctionName
Case "CountChar"
Return CountChar(DirectCast(Args(0), XPathNodeIterator), CChar(Args(1)))
Case "FindTaskBy"
Return FindTaskBy(DirectCast(Args(0), XPathNodeIterator), CStr(Args(1).ToString()))
Case "Left"
Return Left(CStr(Args(0)), CInt(Args(1)))
Case "Right"
Return Right(CStr(Args(0)), CInt(Args(1)))
Case Else
End Select
' Return Nothing for unknown function name.
Return Nothing
End Function
' XPath extension functions.
Private Function CountChar(ByVal Node As XPathNodeIterator, ByVal CharToCount As Char) As Integer
Dim CharCount As Integer = 0
For CharIndex As Integer = 0 To Node.Current.Value.Length - 1
If Node.Current.Value(CharIndex) = CharToCount Then
CharCount += 1
End If
Next
Return CharCount
End Function
' This overload will not force the user
' to cast to string in the xpath expression
Private Function FindTaskBy(ByVal Node As XPathNodeIterator, ByVal Text As String) As String
If (Node.Current.Value.Contains(Text)) Then
Return Node.Current.Value
Else
Return ""
End If
End Function
End Class
次のコードでは IXsltContextVariable を実装します。 このクラスは、実行時に XPath クエリ式内のユーザー定義変数への参照を解決します。 このクラスのインスタンスは、カスタム クラス ResolveVariable の、オーバーライドされた XsltContext メソッドによって作成され、返されます。
コード コメントでは、クラスのメンバーを示しています。
// The interface used to resolve references to user-defined variables
// in XPath query expressions at run time. An instance of this class
// is returned by the overridden ResolveVariable function of the
// custom XsltContext class.
public class XPathExtensionVariable : IXsltContextVariable
{
// Namespace of user-defined variable.
private string prefix;
// The name of the user-defined variable.
private string varName;
// Constructor used in the overridden ResolveVariable function of custom XsltContext.
public XPathExtensionVariable(string prefix, string varName)
{
this.prefix = prefix;
this.varName = varName;
}
// Function to return the value of the specified user-defined variable.
// The GetParam method of the XsltArgumentList property of the active
// XsltContext object returns value assigned to the specified variable.
public object Evaluate(System.Xml.Xsl.XsltContext xsltContext)
{
XsltArgumentList vars = ((CustomContext)xsltContext).ArgList;
return vars.GetParam(varName, prefix);
}
// Determines whether this variable is a local XSLT variable.
// Needed only when using a style sheet.
public bool IsLocal
{
get
{
return false;
}
}
// Determines whether this parameter is an XSLT parameter.
// Needed only when using a style sheet.
public bool IsParam
{
get
{
return false;
}
}
public System.Xml.XPath.XPathResultType VariableType
{
get
{
return XPathResultType.Any;
}
}
}
' The interface used to resolve references to user-defined variables
' in XPath query expressions at run time. An instance of this class
' is returned by the overridden ResolveVariable function of the
' custom XsltContext class.
Public Class XPathExtensionVariable
Implements IXsltContextVariable
' Namespace of user-defined variable.
Private m_Prefix As String
' The name of the user-defined variable.
Private m_VarName As String
' Constructor used in the overridden ResolveVariable function of custom XsltContext.
Public Sub New(ByVal Prefix As String, ByVal VarName As String)
m_Prefix = Prefix
m_VarName = VarName
End Sub
' Function to return the value of the specified user-defined variable.
' The GetParam method of the XsltArgumentList property of the active
' XsltContext object returns value assigned to the specified variable.
Public Function Evaluate(ByVal Context As XsltContext) As Object Implements IXsltContextVariable.Evaluate
Dim vars As XsltArgumentList = DirectCast(Context, CustomContext).ArgList
Return vars.GetParam(m_VarName, m_Prefix)
End Function
' Determines whether this variable is a local XSLT variable.
' Needed only when using a style sheet.
Public ReadOnly Property IsLocal() As Boolean Implements IXsltContextVariable.IsLocal
Get
Return False
End Get
End Property
' Determines whether this parameter is an XSLT parameter.
' Needed only when using a style sheet.
Public ReadOnly Property IsParam() As Boolean Implements IXsltContextVariable.IsParam
Get
Return False
End Get
End Property
Public ReadOnly Property VariableType() As XPathResultType Implements IXsltContextVariable.VariableType
Get
Return XPathResultType.Any
End Get
End Property
End Class
前のクラス定義がスコープ内にある次のコードでは、カスタム関数を使用して Tasks.xml
ドキュメントの要素内の文字をカウントします。 コード コメントでは、カスタム関数をコンパイルし、それを Tasks.xml
ドキュメントに対して実行するコードについて説明しています。
using System;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.IO;
namespace XPathExtensionFunctions
{
class Program
{
static void Main(string[] args)
{
char keychar = ' ';
while (true)
{
Console.Write("\r\nEnter key character to count in names: ");
keychar = Console.ReadKey().KeyChar;
if (keychar.Equals('q')) return;
try
{
// Load source XML into an XPathDocument object instance.
XPathDocument xmldoc = new XPathDocument("Tasks.xml");
// Create an XPathNavigator from the XPathDocument.
XPathNavigator nav = xmldoc.CreateNavigator();
//Create argument list and add the parameters.
XsltArgumentList varList = new XsltArgumentList();
varList.AddParam("charToCount", string.Empty, keychar);
// Create an instance of custom XsltContext object.
// Pass in the XsltArgumentList object
// in which the user-defined variable will be defined.
CustomContext context = new CustomContext(new NameTable(), varList);
// Add a namespace definition for the namespace prefix that qualifies the
// user-defined function name in the query expression.
context.AddNamespace("Extensions", "http://xpathExtensions");
// Create the XPath expression using extension function to select nodes
// that contain 2 occurrences of the character entered by user.
XPathExpression xpath = XPathExpression.Compile(
"/Tasks/Name[Extensions:CountChar(., $charToCount) = 2]");
xpath.SetContext(context);
XPathNodeIterator iter = nav.Select(xpath);
if (iter.Count.Equals(0))
Console.WriteLine("\n\n\rNo results contain 2 instances of "
+ keychar.ToString());
else
{
Console.WriteLine("\n\n\rResults that contain 2 instances of : "
+ keychar.ToString());
// Iterate over the selected nodes and output the
// results filtered by extension function.
while (iter.MoveNext())
{
Console.WriteLine(iter.Current.Value);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Imports System.Xml
Imports System.Xml.XPath
Imports System.Xml.Xsl
Imports System.IO
Module Program
Sub Main()
Dim KeyChar As Char = " "
While (True)
Console.WriteLine()
Console.Write("Enter key character to count in names: ")
KeyChar = Console.ReadKey().KeyChar()
If KeyChar = "q" Then Return
Try
' Load source XML into an XPathDocument object instance.
Dim XmlDoc As New XPathDocument("Tasks.xml")
' Create an XPathNavigator from the XPathDocument.
Dim Navigator As XPathNavigator = XmlDoc.CreateNavigator()
' Create argument list and add the parameters.
Dim VarList As New XsltArgumentList()
VarList.AddParam("charToCount", "", KeyChar)
' Create an instance of custom XsltContext object.
' Pass in the XsltArgumentList object in which
' the user-defined variable will be defined.
Dim Context As New CustomContext(New NameTable(), VarList)
' Add a namespace definition for the namespace prefix that qualifies
' the user-defined function name in the query expression.
Context.AddNamespace("Extensions", "http://xpathExtensions")
' Create the XPath expression using extension function select nodes
' that contain 3 occurrences of the character entered by user.
Dim XPath As XPathExpression = _
XPathExpression.Compile("/Tasks/Name[Extensions:CountChar(., $charToCount) = 2]")
XPath.SetContext(Context)
Dim Iterator As XPathNodeIterator = Navigator.Select(XPath)
Console.WriteLine(vbCrLf)
If Iterator.Count = 0 Then
Console.WriteLine("No results contain 2 instances of {0}.", _
KeyChar.ToString())
Else
Console.WriteLine("Results that contain 2 instances of {0}: ", _
KeyChar.ToString())
' Iterate over the selected nodes and output
' the results filtered by extension function.
While Iterator.MoveNext()
Console.WriteLine(Iterator.Current.Value)
End While
end if
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End While
End Sub
End Module
この例では、次の XML データを使用しています。
<?xml version="1.0" encoding="utf-8" ?>
<Tasks>
<Name>Reserve orders by customer</Name>
<Name>Reserve orders by region</Name>
<Name>Reserve orders by phone number</Name>
<Name>Reserve orders by priority</Name>
<Name>Total orders by customer</Name>
<Name>Total orders by region</Name>
<Name>Total orders by phone number</Name>
<Name>Total orders by priority</Name>
<Name>Schedule delivery by customer</Name>
<Name>Schedule delivery by region</Name>
<Name>Schedule delivery by phone number</Name>
<Name>Schedule delivery by priority</Name>
<Name>Follow up delivery by customer</Name>
<Name>Follow up delivery by region</Name>
<Name>Follow up delivery by phone number</Name>
<Name>Follow up delivery by priority</Name>
</Tasks>
.NET