2010-10-07 35 views
9

Tôi có một cuộc gọi một cách hợp lý phức tạp để xsl: apply-templates:Cách sử dụng biến XSL trong xsl: apply-templates?

<xsl:apply-templates select="columnval[@id 
             and not(@id='_Name_') 
             and not(@id='Group') 
             and not(@id='_Count_')]"/> 

Khái niệm được tái sử dụng ở những nơi khác như thế này:

<xsl:apply-templates select="someothernode[@id 
              and not(@id='_Name_') 
              and not(@id='Group') 
              and not(@id='_Count_')]"/> 

tôi muốn khái quát nó bằng cách nào đó, vì vậy tôi có thể xác định nó một lần và tái sử dụng nó ở nơi khác. Tuy nhiên, điều này dường như không hoạt động:

<xsl:variable name="x">@id and not(@id='_Name_') and not(@id='Group') and not(@id='_Count_')</xsl:variable> 
<xsl:apply-templates select="columnval[$x]"/> 
<xsl:apply-templates select="someothernode[$x]"/> 

Có cách nào khác hay hơn để làm việc này? Tất cả những gì tôi muốn là sử dụng lại biểu thức xpath trong nhiều cuộc gọi khác nhau tới xsl: apply-templates (một số trong đó chọn từ các trẻ khác nhau).

Điều này sẽ được sử dụng trong ứng dụng khách, vì vậy tôi không thể sử dụng bất kỳ tiện ích mở rộng nào hoặc chuyển sang XSLT 2 một cách không may. :(

Cảm ơn.

+0

Tốt câu hỏi. Xem câu trả lời của tôi cho một mô tả về hai giải pháp khả thi (XSLT 1.0 và XSLT 2.0) và gợi ý một giải pháp mạnh mẽ hơn, sử dụng các hàm bậc cao hơn. –

Trả lời

5

Bạn không thể xây dựng XPath động trong XSLT (ít nhất, không phải XSLT 1.0). Nhưng bạn có thể dễ dàng hoàn thành những gì bạn đang cố gắng thực hiện bằng các chế độ mẫu:

<xsl:apply-templates select="columnval" mode="filter"/> 
<xsl:apply-template select="someothernode" mode="filter"/> 

... 

<!-- this guarantees that elements that don't match the filter don't get output --> 
<xsl:template match="*" mode="filter"/> 

<xsl:template match="*[@id and not(@id='_Name_') and not(@id='Group') and not(@id='_Count_')]" mode="filter"> 
    <xsl:apply-templates select="." mode="filtered"/> 
</xsl:template> 

<xsl:template match="columnval" mode="filtered"> 
    <!-- this will only be applied to the columnval elements that pass the filter --> 
</xsl:template> 

<xsl:template match="someothernode" mode="filtered"> 
    <!-- this will only be applied to the someothernode elements that pass the filter --> 
</xsl:template> 
+0

+1 đây là một cách tiếp cận hiệu quả Chỉ có nhược điểm là bộ lọc được mã hóa cứng và do đó không thay đổi Nếu một bộ lọc biến không cần thiết, tôi sẽ đi với giải pháp này – Tomalak

+0

Chế độ mẫu không có ý nghĩa với tôi cho đến khi tôi bắt đầu chạy vào các vấn đề như OP's –

1

tôi sẽ xem xét sử dụng một phần mở rộng để XSLT. Tôi không nghĩ rằng bạn có thể làm điều đó trong XSLT "chuẩn".

Phần mở rộng này có thể làm những gì bạn muốn : http://www.exslt.org/dyn/functions/evaluate/index.html

+0

Câu hỏi được cập nhật - chúng tôi không thể sử dụng tiện ích mở rộng, vì chúng tôi đang dựa vào MSXML để thực hiện chuyển đổi: ( – Colen

+0

'msxsl: node-set' sẽ hoạt động, sau đó là –

1

với exsl mở rộng: nodeset, bạn có thể tạo một mẫu tên là chấp nhận một nodeset $ x và trả về lọc nodeset theo ngữ tĩnh của bạn

bạn cũng có thể xác định một chức năng, trong XSLT 2.0. .

+0

Câu hỏi được cập nhật - không may chúng tôi bị mắc kẹt với XSLT 1.0.: ( – Colen

1

Làm thế nào về:

<xsl:variable name="filter" select="_Name_|Group|_Count_" /> 

<xsl:apply-templates select="columnval" mode="filtered" /> 
<xsl:apply-templates select="someothernode" mode="filtered" /> 

<xsl:template match="someothernode|columnval" mode="filtered"> 
    <xsl:if test="not(contains(
    concat('|', $filter,'|'), 
    concat('|', @id,'|'), 
))"> 
    <!-- whatever --> 
    </xsl:if> 
</xsl:template> 

Bạn có thể làm $filter một param, và vượt qua nó từ bên ngoài ví dụ.

Điều bạn không thể làm (như bạn đã nhận thấy) là sử dụng các biến để lưu trữ các biểu thức XPath.

+0

Câu hỏi ngu ngốc - tại sao bạn có thể làm điều đó với các tham số, nhưng không phải là biến? – Colen

+0

@Colen: Biến (hoặc param, cho rằng vấn đề) '$ filter' lưu trữ một chuỗi, không phải là một biểu thức. delimiter. ;-) – Tomalak

1

Cả XSLT 1.0 và XSLT 2.0 đều không hỗ trợ đánh giá động.

Một cách để thực hiện việc này là sử dụng <xsl:function> trong XSLT 2.0 hoặc <xsl:call-template> trong XSLT 1.0.

<xsl:function name="my:test" as="xs:boolean"> 
    <xsl:param name="pNode" as="element()"/> 

    <xsl:variable name="vid" select="$pNode/@id"/> 

    <xsl:sequence select= 
    "$vid and not($vid=('_Name_','Group','_Count_')"/> 
</xsl:function> 

sau đó bạn có thể sử dụng chức năng này:

<xsl:apply-templates select="columnval[my:test(.)]"/> 

Chắc chắn, bạn có thể chỉ định các xét nghiệm trong mô hình phù hợp cụ thể theo đề nghị của Robert Rossney, và điều này có thể là cách tốt nhất.

Trong trường hợp bạn cần phải động xác định những chức năng lọc để sử dụng, một công cụ mạnh mẽ là FXSL thư viện, mà thực hiện đặt hàng-Chức năng cao hơn (HOF) trong XSLT. HOF là các hàm chấp nhận các hàm khác làm tham số và có thể trả về một hàm làm kết quả của chúng.

Sử dụng phương pháp này, bạn tự động xác định và chuyển đến my:test() làm thông số một hàm thực hiện phép thử.

2

Refactoring @Robert Rossney và @Tomalak

<xsl:apply-templates select="columnval" mode="filter"/> 
<xsl:apply-templates select="someothernode" mode="filter"/> 

<xsl:template match="*" mode="filter"> 
    <xsl:param name="pFilter" select="'_Name_|Group|_Count_'"/> 
    <xsl:apply-templates select="self::* 
           [not(contains( 
             concat('|',$pFilter,'|'), 
             concat('|',@id,'|'))) 
           and @id]"/> 
</xsl:template> 
+1

Vì các thuộc tính được định nghĩa bởi các thành phần của các phần tử, bạn có thể thay thế nút 'node()' chung bằng '*'. :-) – Tomalak