给xpath添加正则表达式匹配函数
做网页解析时,将html转成xml格式之后,再利用xpath则可以轻易地截取任何所需要的数据。在使用xpath时,常常会用到其中的一些函数,特别是字符串函数,完整的函数列表可在w3school找到:http://www.w3school.com.cn/xpath/xpath_functions.asp#string)。然而这仅仅是标准xpath里所提供的,dotnet里这只实现了一部分,特别是本标题所提到的正则匹配函数fn:matches(string,pattern),dotnet里竟然不支持。虽然dotnnet里还是能用诸如contains等判断函数,但比起强大的正则表达式,其实现的功能远远不能满足要求。
为了能够更加灵活、方便的匹配字符串,需要给xpath添加自定义的正则匹配函数,幸好dotnet提供了接口,先看看这些基类及接口。
XsltContext
封装可扩展样式表转换语言 (XSLT) 处理器的当前执行上下文,使 XML 路径语言 (XPath) 在 XPath 表达式中解析函数、参数和命名空间。需要定义一个继承XsltContext的子类,实现对自定义函数的调用。
IXsltContextFunction
为在运行库执行期间在可扩展样式表转换语言 (XSLT) 样式表中定义的给定函数提供一个接口。实现该接口的子类定义以及提供自定义函数的功能。
IXsltContextVariable
为在运行库执行期间在样式表中定义的给定变量提供一个接口。其子类实现在调用自定义函数时,参数值的计算。
涉及类比较多,但是在具体实现远没有那么复杂,后续添加其他函数也很方便。
关键代码
实现XsltContext
public class XpathContext : XsltContext
{
// XsltArgumentList to store my user defined variables
private XsltArgumentList m_ArgList;
// Constructors
public XpathContext()
{ }
public XpathContext(NameTable nt)
: base(nt)
{
}
public XpathContext(NameTable nt, XsltArgumentList argList)
: base(nt)
{
m_ArgList = argList;
}
// Returns the XsltArgumentList that contains custom variable definitions.
public XsltArgumentList ArgList
{
get
{
return m_ArgList;
}
}
// Function to resolve references to my custom functions.
public override IXsltContextFunction ResolveFunction(string prefix,
string name, XPathResultType[] ArgTypes)
{
XPathExtensionFunction func = null;
// Create an instance of appropriate extension function class.
switch (name)
{
// 匹配正则表达式, XPath1.0没有该方法
case "Match":
func = new XPathExtensionFunction("Match", 1, 2, new
XPathResultType[] { XPathResultType.NodeSet, XPathResultType.String }, XPathResultType.Boolean);
break;
// 去除空格
case "Trim":
func = new XPathExtensionFunction("Trim", 1, 1,
new XPathResultType[] { XPathResultType.String }, XPathResultType.String);
break;
default:
throw new ArgumentException("没有定义" + name + "函数");
}
return func;
}
// Function to resolve references to my custom variables.
public override IXsltContextVariable ResolveVariable(string prefix, string name)
{
// Create an instance of an XPathExtensionVariable.
XPathExtensionVariable Var;
Var = new XPathExtensionVariable(name);
return Var;
}
public override int CompareDocument(string baseUri, string nextbaseUri)
{
return 0;
}
public override bool PreserveWhitespace(XPathNavigator node)
{
return true;
}
public override bool Whitespace
{
get
{
return true;
}
}
}
{
// XsltArgumentList to store my user defined variables
private XsltArgumentList m_ArgList;
// Constructors
public XpathContext()
{ }
public XpathContext(NameTable nt)
: base(nt)
{
}
public XpathContext(NameTable nt, XsltArgumentList argList)
: base(nt)
{
m_ArgList = argList;
}
// Returns the XsltArgumentList that contains custom variable definitions.
public XsltArgumentList ArgList
{
get
{
return m_ArgList;
}
}
// Function to resolve references to my custom functions.
public override IXsltContextFunction ResolveFunction(string prefix,
string name, XPathResultType[] ArgTypes)
{
XPathExtensionFunction func = null;
// Create an instance of appropriate extension function class.
switch (name)
{
// 匹配正则表达式, XPath1.0没有该方法
case "Match":
func = new XPathExtensionFunction("Match", 1, 2, new
XPathResultType[] { XPathResultType.NodeSet, XPathResultType.String }, XPathResultType.Boolean);
break;
// 去除空格
case "Trim":
func = new XPathExtensionFunction("Trim", 1, 1,
new XPathResultType[] { XPathResultType.String }, XPathResultType.String);
break;
default:
throw new ArgumentException("没有定义" + name + "函数");
}
return func;
}
// Function to resolve references to my custom variables.
public override IXsltContextVariable ResolveVariable(string prefix, string name)
{
// Create an instance of an XPathExtensionVariable.
XPathExtensionVariable Var;
Var = new XPathExtensionVariable(name);
return Var;
}
public override int CompareDocument(string baseUri, string nextbaseUri)
{
return 0;
}
public override bool PreserveWhitespace(XPathNavigator node)
{
return true;
}
public override bool Whitespace
{
get
{
return true;
}
}
}
实现IXsltContextFunction
/// <summary>
/// 自定义正则表达式函数
/// </summary>
public class XPathExtensionFunction : IXsltContextFunction
{
private XPathResultType[] m_ArgTypes;
private XPathResultType m_ReturnType;
private string m_FunctionName;
private int m_MinArgs;
private int m_MaxArgs;
/// <summary>
/// 获取函数的参数的最小数目。这使用户能够区分重载函数
/// </summary>
public int Minargs
{
get
{
return m_MinArgs;
}
}
/// <summary>
/// 获取函数的参数的最大数目。这使用户能够区分重载函数
/// </summary>
public int Maxargs
{
get
{
return m_MaxArgs;
}
}
/// <summary>
/// 获取为函数的参数列表提供的 XML 路径语言 (XPath) 类型。该信息可用于发现函数的签名,该签名使您能够区分重载函数
/// </summary>
public XPathResultType[] ArgTypes
{
get
{
return m_ArgTypes;
}
}
/// <summary>
/// 获取 XPathResultType,它表示函数返回的 XPath 类型
/// </summary>
public XPathResultType ReturnType
{
get
{
return m_ReturnType;
}
}
/// <summary>
/// 创建自定义函数的实例
/// </summary>
/// <param name="name">方法名称</param>
/// <param name="minArgs">最小参数个数</param>
/// <param name="maxArgs">最大参数个数</param>
/// <param name="argTypes">参数类型数组</param>
/// <param name="returnType">返回类型</param>
public XPathExtensionFunction(string name, int minArgs, int maxArgs, XPathResultType[] argTypes, XPathResultType returnType)
{
m_FunctionName = name;
m_MinArgs = minArgs;
m_MaxArgs = maxArgs;
m_ArgTypes = argTypes;
m_ReturnType = returnType;
}
// 在运行时调用
public object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
{
// The two custom XPath extension functions
switch (m_FunctionName)
{
case "Match":
// 调用正则匹配 参数一为正则表达式
return Regex.IsMatch(docContext.InnerXml, args[0].ToString());
case "Trim":
return docContext.Value.Trim();
default:
throw new ArgumentException("没有定义" + m_FunctionName + "函数");
}
}
}
/// 自定义正则表达式函数
/// </summary>
public class XPathExtensionFunction : IXsltContextFunction
{
private XPathResultType[] m_ArgTypes;
private XPathResultType m_ReturnType;
private string m_FunctionName;
private int m_MinArgs;
private int m_MaxArgs;
/// <summary>
/// 获取函数的参数的最小数目。这使用户能够区分重载函数
/// </summary>
public int Minargs
{
get
{
return m_MinArgs;
}
}
/// <summary>
/// 获取函数的参数的最大数目。这使用户能够区分重载函数
/// </summary>
public int Maxargs
{
get
{
return m_MaxArgs;
}
}
/// <summary>
/// 获取为函数的参数列表提供的 XML 路径语言 (XPath) 类型。该信息可用于发现函数的签名,该签名使您能够区分重载函数
/// </summary>
public XPathResultType[] ArgTypes
{
get
{
return m_ArgTypes;
}
}
/// <summary>
/// 获取 XPathResultType,它表示函数返回的 XPath 类型
/// </summary>
public XPathResultType ReturnType
{
get
{
return m_ReturnType;
}
}
/// <summary>
/// 创建自定义函数的实例
/// </summary>
/// <param name="name">方法名称</param>
/// <param name="minArgs">最小参数个数</param>
/// <param name="maxArgs">最大参数个数</param>
/// <param name="argTypes">参数类型数组</param>
/// <param name="returnType">返回类型</param>
public XPathExtensionFunction(string name, int minArgs, int maxArgs, XPathResultType[] argTypes, XPathResultType returnType)
{
m_FunctionName = name;
m_MinArgs = minArgs;
m_MaxArgs = maxArgs;
m_ArgTypes = argTypes;
m_ReturnType = returnType;
}
// 在运行时调用
public object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
{
// The two custom XPath extension functions
switch (m_FunctionName)
{
case "Match":
// 调用正则匹配 参数一为正则表达式
return Regex.IsMatch(docContext.InnerXml, args[0].ToString());
case "Trim":
return docContext.Value.Trim();
default:
throw new ArgumentException("没有定义" + m_FunctionName + "函数");
}
}
}
具体调用(代码中连接来源于国家旅游局)
static void Main(string[] args)
{
string xml = @"
<links>
<a href=""http://www.cnta.gov.cn/html/2009-12/2009-12-3-16-30-23909.html"">2009年1-9月主要城市接待情况(二)</a>
<br/>
<a href=""http://www.cnta.gov.cn/html/2009-10/2009-10-16-10-2-71826.html"">2009年1-8月主要城市接待情况(二)</a>
<br/>
<a href=""http://www.cnta.gov.cn/html/2009-10/2009-10-16-10-2-71826.html"">2009年1-8月主要城市接待情况(二)</a>
</links>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XpathContext xpathContext = new XpathContext();
XmlNode node = doc.SelectSingleNode(@"//a[Match('\d{4}年\d{1,2}-\d{1,2}月主要城市接待情况\(二\)')]", xpathContext);
Console.WriteLine(node.Attributes["href"].Value);
}
{
string xml = @"
<links>
<a href=""http://www.cnta.gov.cn/html/2009-12/2009-12-3-16-30-23909.html"">2009年1-9月主要城市接待情况(二)</a>
<br/>
<a href=""http://www.cnta.gov.cn/html/2009-10/2009-10-16-10-2-71826.html"">2009年1-8月主要城市接待情况(二)</a>
<br/>
<a href=""http://www.cnta.gov.cn/html/2009-10/2009-10-16-10-2-71826.html"">2009年1-8月主要城市接待情况(二)</a>
</links>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XpathContext xpathContext = new XpathContext();
XmlNode node = doc.SelectSingleNode(@"//a[Match('\d{4}年\d{1,2}-\d{1,2}月主要城市接待情况\(二\)')]", xpathContext);
Console.WriteLine(node.Attributes["href"].Value);
}