четверг, 14 апреля 2011 г.

Динамические XPath запросы или как составить запрос при помощи C#

Как то разрабатывал я каталог автосервисов и нужно было сделать фильтр, что бы можно было выбрать автосервис по округу, марке автомобиля, времени работы наличию тех или иных услуг. Я решил, что параметры должны передаваться через URL-адрес. Для того что бы это реализовать нужно сделать очень простую вещь (кажется что простую;-) обработать URL запрос и составить XPath выражения для того что бы вытащить нужные страницы.

Но оказалось, что составить XPath в XLST ой, как не просто! Тогда я решал написать свою функцию на C# и подключить её.

Сделал проект DLL'ка, там сделал один единственный класс, у класса статическую функцию, которая разбирала URL и состоаляла XPath запрос, и возвращала строку. Пример:

namespace alfa.Umbraco
{
    public class XPath
    {
        public static string GetRequestString()
        {
                HttpRequest Request = HttpContext.Current.Request;
                if (!string.IsNullOrWhiteSpace(Request.QueryString["a"]))
                    return string.Format(" IsMy = '{0}'", Request.QueryString["a"]);

            return " 1=1";           
        }
}
}

Подключил dll'ку к проекту. Добавляем код в XSLT и в итоге ничего не работает((( Вопрос почему?

Все очень просто! Дело в том, что результат функции трактуется как СРОКА, а <xsl:for-each select="XPATH"> ждет от Вас XPath строку!!!

Я потратил целый вечер, что бы победить эту проблему. И нашел решение! Давай те проделаем все по шагам.

1. Нужно создать библиотеку с функцией, которая нам поможет:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.XPath;

namespace alfa.Umbraco
{
    public class XPath
    {
        public static string evalXpath(XPathNodeIterator nodes, string xpath)
        {
            string add = "";
            HttpRequest Request = HttpContext.Current.Request;
            if (!string.IsNullOrWhiteSpace(Request.QueryString["a"]))
                add = string.Format(" and string(isMy) = '{0}'", Request.QueryString["a"]);
            else
                add = " and 1=1";

            try
            {
                while (nodes.MoveNext())
                {
                    XPathNavigator n = nodes.Current as XPathNavigator;
                    return n.Evaluate(xpath + add).ToString();
                }
            }
            catch
            {
                return null;
            }
            return null;
        }
    }
}
Суть этой функции очень простая, в неё мы передаем документа, и xpath запрос (можем не передавать), в первой части функции мы входящий XPath запрос доделываем и проверяем соответствует ли наша сущность этому запросу, в результате возвращаем строку "True" или "False".

2. Подключаем эту эту библиотеку в Umbraco.
3. А XSLT будет выглядеть примерно следующим образом:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#x00A0;"> ]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library"
  xmlns:alfa.XPath="urn:alfa.XPath"
xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon"
xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes"
xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath"
xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions"
xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings"
xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets"
  exclude-result-prefixes="alfa.XPath msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets ">

<xsl:output method="xml" omit-xml-declaration="yes" />

<xsl:param name="currentPage"/>

<!-- Input the documenttype you want here -->
<xsl:variable name="level" select="1"/>
   
<xsl:template match="/">
 
<!-- The fun starts here -->
<ul class="menu">
  <xsl:for-each select="$currentPage/ancestor-or-self::* [@level=;level]/* [alfa.XPath:evalXpath(.) = 'True']">
  <li>
    <a href="{umbraco.library:NiceUrl(@id)}">
      <xsl:value-of select="@nodeName"/>
    </a>
  </li>
</xsl:for-each>
</ul>

</xsl:template>

</xsl:stylesheet>

Готово!

Комментариев нет:

Отправить комментарий