Я создаю инструмент, который выполняет запросы xpath 1.0 для документов XHTML. Требование использовать префикс пространства имен в запросе убивает меня. Запрос выглядит так:
html/body/div[@class='contents']/div[@class='body']/
div[@class='pgdbbyauthor']/h2[a[@name][starts-with(.,'Quick')]]/
following-sibling::ul[1]/li/a
(все в одной строке)
... что достаточно плохо, за исключением того, что это xpath 1.0, мне нужно использовать явный префикс пространства имен для каждого QName, поэтому это выглядит так:
ns1:html/ns1:body/ns1:div[@class='contents']/ns1:div[@class='body']/
ns1:div[@class='pgdbbyauthor']/ns1:h2[ns1:a[@name][starts-with(.,'Quick')]]/
following-sibling::ns1:ul[1]/ns1:li/ns1:a
Чтобы настроить запрос, я делаю что-то вроде этого:
var xpathDoc = new XPathDocument(new StringReader(theText));
var nav = xpathDoc.CreateNavigator();
var xmlns = new XmlNamespaceManager(nav.NameTable);
foreach (string prefix in xmlNamespaces.Keys)
xmlns.AddNamespace(prefix, xmlNamespaces[prefix]);
XPathNodeIterator selection = nav.Select(xpathExpression, xmlns);
Но я хочу, чтобы xpathExpression использовал неявное пространство имен по умолчанию.
Есть ли способ для меня преобразовать неукрашенное выражение xpath после того, как оно было написано, чтобы вставить префикс пространства имен для каждого имени элемента в запросе?
Я думаю, что-нибудь между двумя слешами, я мог бы вставить туда префикс. Исключая имена осей курса, такие как «parent ::» и «previous-sibling ::». И подстановочные знаки. Вот что я имею в виду под " finagle пространством имен по умолчанию".
Этот хак сработает?
Добавление
Вот что я имею в виду. Предположим, у меня есть выражение xpath, и, прежде чем передать его в nav.Select (), я преобразую его. Примерно так:
string FixupWithDefaultNamespace(string expr)
{
string s = expr;
s = Regex.Replace(s, "^(?!::)([^/:]+)(?=/)", "ns1:$1"); // beginning
s = Regex.Replace(s, "/([^/:]+)(?=/)", "/ns1:$1"); // stanza
s = Regex.Replace(s, "::([A-Za-z][^/:*]*)(?=/)", "::ns1:$1"); // axis specifier
s = Regex.Replace(s, "\\[([A-Za-z][^/:*\\(]*)(?=[\\[\\]])", "[ns1:$1"); // predicate
s = Regex.Replace(s, "/([A-Za-z][^/:]*)(?!<::)$", "/ns1:$1"); // end
s = Regex.Replace(s, "^([A-Za-z][^/:]*)$", "ns1:$1"); // edge case
s = Regex.Replace(s, "([-A-Za-z]+)\\(([^/:\\.,\\)]+)(?=[,\\)])", "$1(ns1:$2"); // xpath functions
return s;
}
На самом деле это работает для простых случаев, которые я пробовал. Чтобы использовать приведенный выше пример - если вход является первым выражением xpath, я получаю вывод 2-й со всеми префиксами ns1
. На самом деле вопрос в том, стоит ли надеяться, что этот подход Regex.Replace сработает, поскольку выражения xpath усложняются?