다음을 통해 공유


사용자 정의 함수 및 변수

XPathNavigator 클래스는 XPathDocument 데이터와 상호 작용하는 데 사용되는 메서드 집합을 제공합니다. XPath 쿼리 식에서 사용되는 확장명 함수 및 변수를 구현하여 표준 XPath 함수를 보완할 수 있습니다. SetContext 메서드는 XsltContext에서 파생되는 사용자 정의 컨텍스트를 적용할 수 있습니다. 사용자 정의 함수는 사용자 지정 컨텍스트로 확인됩니다.

확장명 함수 및 변수는 XML 삽입 공격을 방지하는 데 유용할 수 있습니다. 이러한 시나리오에서 사용자 입력은 처리 명령과 연결된 원시 입력이 아닌 사용자 지정 변수로 할당되고 확장명 함수로 처리됩니다. 확장 함수 및 변수에는 사용자 입력이 포함되어 있어서 디자이너에서 의도한 대로 XML 데이터에 대해서만 작업을 수행합니다.

확장을 사용하려면 확장명 함수 및 변수를 지원하는 인터페이스 XsltContextIXsltContextFunction과 함께 사용자 지정 IXsltContextVariable 클래스를 구현합니다. XPathExpression은 사용자 입력을 XsltArgumentList와 함께 사용자 지정 XsltContext에 추가합니다.

XPathExpressionXPathNavigator가 식에서 식별된 노드를 검색 및 처리하는 데 사용하는 컴파일된 쿼리를 나타냅니다.

다음 예제는 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>