динамический путь в xslt? - PullRequest
       4

динамический путь в xslt?

4 голосов
/ 07 января 2011

У меня есть следующий набор файлов:

SourceFile.xml:

      <?xml version="1.0" encoding="utf-8" ?>
     <Employees>
     <Employee id="1">
          <firstname relationship="headnote">Atif</firstname>
          <lastname relationship="lname">Bashir</lastname>
          <age relationship="age">32</age>
          </Employee>
     </Employees>

ParamerterSettings.xml

        <?xml version="1.0" encoding="utf-8"?>
        <Settings>
        <Employee id="1">
             <sourceFile>Lookup1.xml</sourceFile>
             <sourceXpathfield>Employees/Employee[@id</sourceXpathfield>
             <lookupXpathfield>Employees/Employee[@id='1']</lookupXpathfield>
             <elementstoinsert>xyz</elementstoinsert>
             </Employee>
         </Settings>

Lookup.xml

<?xml version="1.0" encoding="utf-8"?>
 <Employees>
  <Employee id="1">
      <department code="102">HR</department>
   </Employee>
   </Employees>

transform.xsl

  <?xml version="1.0" encoding="UTF-8" ?>
   <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

   <xsl:include href="identity.xsl"/>

  <xsl:param name="EmployeeId" select="'1,2'" />
  <xsl:variable name="FileSettings" select="document('test3.xml')" />
  <xsl:variable name="SuppressSetting" select="$FileSettings/Settings/Employee[@id = tokenize($EmployeeId, ',')]" />

  <xsl:template match="Employee">
  <xsl:copy>
  <xsl:apply-templates select="@*"/>
  <xsl:apply-templates select="publisher" />
  <xsl:apply-templates select="node() except publisher"/>
  <xsl:variable name="outerfile" select="document($SuppressSetting/sourceFile)"></xsl:variable>
  <xsl:variable name="outerfiledetails" select="$outerfile/$SuppressSetting/lookupXpathfield"></xsl:variable>
  <xsl:value-of select="$outerfiledetails"></xsl:value-of>
</xsl:copy>
</xsl:template>

</xsl:stylesheet> 

Выходные данные должны быть:

     <?xml version="1.0" encoding="utf-8" ?>
     <Employees>
     <Employee id="1">
          <firstname relationship="headnote">Atif</firstname>
          <lastname relationship="lname">Bashir</lastname>
          <age relationship="age">32</age>
          HR
          </Employee>
     </Employees>

Я изменил следующую строку в Transform.xsl

<xsl:variable name="outerfiledetails" select="$outerfile/$SuppressSetting/lookupXpathfield"></xsl:variable>

на

<xsl:variable name="outerfiledetails" select="$outerfile/Employees/Employee[@id='1']"></xsl:variable>

затем я получаю свой вывод, но я хочу сохранить выражение XPath для SourceFile.xml и Lookup.xml в ParamerterSettings.xml, чтобы я мог написать более общий сценарий.Можно ли это сделать любым другим способом, кроме динамического xpath?Любая идея или намек на то же самое будут высоко оценены.

Ответы [ 3 ]

11 голосов
/ 10 января 2011

Динамическая оценка XPath невозможна в чистом XSLT 1.0 или 2.0.

Существует как минимум три способа сделать это в "гибридном" решении :

I.Используйте функцию EXSLT dyn:evaluate()

К сожалению, очень немногие процессоры XSLT 1.0 реализуют dyn:evaluate().

II.Обработайте XML-документ с помощью XSLT и сгенерируйте новый XSLT-файл, содержащий выражения XPath, а затем выполните сгенерированное преобразование .

Мало кто так поступает, и, на мой взгляд, это болеесложнее, чем следующее решение.

III.Способ визуализатора XPath работает

Идея такова:

  1. Иметь глобальную переменную в таблице стилей XSLT, определенную какthis :

      <xsl:variable name="vExpression" select="dummy"/>
    
  2. Затем загрузите таблицу стилей в виде XML-документа с использованием DOM и замените атрибут select vExpressionпеременная с фактическим выражением XPath, содержащимся в исходном XML-документе.

  3. Наконец, инициируйте преобразование , используя загруженную в память и динамически обновляемую таблицу стилей xslt.

3 голосов
/ 10 января 2011

Вы не можете сделать это в XSLT 2.0, но вы сможете сделать это в последней версии XSLT:

http://www.w3.org/TR/xslt-21/#element-evaluate

2 голосов
/ 09 августа 2013

Да, мы можем ... по крайней мере, элементарно. Вот обходной путь, который я использую с Saxon CE (XSLT 2.0), пока не будет доступна функция «оценки». Возможно, это не работает для всех видов сложных XML-документов, но, возможно, вы можете настроить «фильтр» так, как вам нужно (запрос по атрибутам и т. Д.).

В моей особой ситуации у меня есть выражения xPath, описывающие «полный» путь к элементам, включая их имена, и хитрость заключается в том, чтобы использовать подстановочный знак в сочетании только с последним элементом динамического выражения xPath, например, используйте «третий» вместо «первый / второй / третий»:

<xsl:variable name="value" select="//*[name() = 'third']" />

Для того, чтобы ограничить результат (выделите все элементы с именем «третий»), вам также придется запросить предков «первый» и «второй». Может быть, у кого-нибудь есть идея упростить следующий код, в частности, вызов предков:

<!-- global variable which holds a XML document with root node "data" -->
<xsl:variable name="record" select="document('record.xml')/data"/>

<!-- select elements from the global "record" variable using dynamic xpath expressions -->
<xsl:function name="utils:evaluateXPath">

    <xsl:param name="xpath" as="xs:string"/>

    <xsl:choose>

        <xsl:when test="function-available('evaluate')">

            <!-- modify the code if the function has been implemented :-) -->
            <xsl:value-of select="'function evaluate() can be used ...'"/>

        </xsl:when>

        <xsl:otherwise>

            <!-- get a list of elements defined in the xpath expression -->
            <xsl:variable name="sequence" select="tokenize($xpath, '/')" />

            <!-- get the number of ancestors for the last element -->
            <xsl:variable name="iAncestors" select="count($sequence)-1" as="xs:integer" />

            <!-- get the last element from the xpath expression -->
            <xsl:variable name="lastElement" select="if ($iAncestors > 0) then $sequence[last()] else $xpath" />

            <!-- try to find the desired element as defined in xpath expression -->
            <!-- use parenthesis to grab only the first occurrence -->
            <xsl:value-of select="
                if ($iAncestors = 0) then
                    ($record//*[name() = $lastElement and not(*)])[1]
                else if ($iAncestors = 1) then
                    ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[1])])[1]
                else if ($iAncestors = 2) then
                    ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[2]) and (name(../..) = $sequence[1])])[1]
                else if ($iAncestors = 3) then
                    ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[3]) and (name(../..) = $sequence[2]) and (name(../../..) = $sequence[1])])[1]
                else if ($iAncestors = 4) then
                    ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[4]) and (name(../..) = $sequence[3]) and (name(../../..) = $sequence[2]) and (name(../../../..) = $sequence[1])])[1]
                else if ($iAncestors = 5) then
                    ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[5]) and (name(../..) = $sequence[4]) and (name(../../..) = $sequence[3]) and (name(../../../..) = $sequence[2]) and (name(../../../../..) = $sequence[1])])[1]
                else 'failure: too much elements for evaluating dyn. xpath ... add another level!'"
            />

        </xsl:otherwise>

    </xsl:choose>

</xsl:function>

Что касается моей цели, то возвращается только первый соответствующий элемент, который не имеет дочерних узлов. Возможно, вам придется настроить это для ваших конкретных потребностей.

...