2009-05-07 28 views
26

Tôi đang xử lý tệp XML nơi tôi muốn giữ số lượng nút, để tôi có thể sử dụng nó làm ID khi tôi viết các nút mới.Trong XSLT làm cách nào để tăng biến toàn cầu từ một phạm vi khác?

Hiện tại tôi có một biến toàn cầu có tên là 'bộ đếm'. Tôi có thể truy cập nó trong một khuôn mẫu, nhưng tôi đã không tìm thấy một cách để thao tác nó trong một khuôn mẫu.

Đây là một phiên bản đặc của tập tin XSLT tôi:

<xsl:variable name="counter" select="1" as="xs:integer"/> 

<xsl:template match="/"> 
    <xsl:for-each select="section"> 
     <xsl:call-template name="section"></xsl:call-template> 
    </xsl:for-each> 
</xsl:template> 

<xsl:template name="section"> 

    <!-- Increment 'counter' here --> 

    <span class="title" id="title-{$counter}"><xsl:value-of select="title"/></span> 
</xsl:template> 

Mọi góp ý thế nào để đi từ đây?

Trả lời

44

Những người khác đã giải thích như thế nào biến là không thay đổi - rằng không có câu lệnh gán trong XSLT (như với ngôn ngữ lập trình hoàn toàn chức năng nói chung).

Tôi có một giải pháp thay thế cho các giải pháp đã được đề xuất cho đến thời điểm này. Nó tránh đi qua tham số (nó dài dòng và xấu xí trong XSLT - thậm chí tôi sẽ thừa nhận điều đó).

Trong XPath, bạn chỉ có thể đếm số <section> yếu tố mà trước hiện thời:

<xsl:template name="section"> 
    <span class="title" id="title-{1 + count(preceding-sibling::section)}"> 
    <xsl:value-of select="title"/> 
    </span> 
</xsl:template> 

(Lưu ý: mã định dạng khoảng trắng sẽ không xuất hiện trong kết quả của bạn, như khoảng trắng chỉ các nút văn bản Vì vậy, đừng cảm thấy bị ép buộc phải đặt các chỉ dẫn trên cùng một dòng.)

Một lợi thế lớn của phương pháp này (trái ngược với việc sử dụng position()) là nó chỉ phụ thuộc vào nút hiện tại, chứ không phải trên danh sách nút hiện tại. Nếu bạn đã thay đổi cách xử lý của mình bằng cách nào đó (ví dụ: do đó, <xsl:for-each> không chỉ xử lý các phần mà còn một số phần tử khác), thì giá trị của position() sẽ không còn tương ứng với vị trí của các yếu tố <section> trong tài liệu của bạn. Mặt khác, nếu bạn sử dụng count() như trên, thì nó sẽ luôn tương ứng với vị trí của mỗi phần tử <section>. Cách tiếp cận này làm giảm sự ghép nối với các phần khác của mã của bạn, mà nói chung là một điều rất tốt.

Cách thay thế để đếm() sẽ là sử dụng hướng dẫn <xsl:number>. Đó là hành vi mặc định sẽ đánh số tất cả các yếu tố như tên cùng cấp, mà sẽ xảy ra là những gì bạn muốn:

<xsl:template name="section"> 
    <xsl:variable name="count"> 
    <xsl:number/> 
    </xsl:variable> 
    <span class="title" id="title-{$count}"> 
    <xsl:value-of select="title"/> 
    </span> 
</xsl:template> 

Đó là một sự đánh đổi trong tính cách rườm rà (đòi hỏi một khai báo biến bổ sung nếu bạn vẫn muốn sử dụng giá trị thuộc tính của các mẫu dấu ngoặc nhọn), nhưng chỉ một chút, vì nó cũng đơn giản hóa đáng kể biểu thức XPath của bạn.

Vẫn còn nhiều chỗ để cải thiện. Mặc dù chúng tôi đã xóa phụ thuộc vào danh sách nút hiện tại nhưng chúng tôi vẫn phụ thuộc vào nút hiện tại. Điều đó, trong và của chính nó, không phải là một điều xấu, nhưng nó không phải ngay lập tức rõ ràng từ nhìn vào mẫu những gì các nút hiện tại là. Tất cả những gì chúng ta biết là mẫu được đặt tên là "section"; để biết chắc chắn những gì đang được xử lý, chúng ta phải tìm nơi khác trong mã của chúng tôi. Nhưng ngay cả điều đó không phải là trường hợp.

Nếu bạn cảm thấy đã dẫn đến sử dụng <xsl:for-each><xsl:call-template> cùng nhau (như trong ví dụ của bạn), hãy quay lại và tìm hiểu cách sử dụng <xsl:apply-templates> thay thế.

<xsl:template match="/doc"> 
    <xsl:apply-templates select="section"/> 
</xsl:template> 

<xsl:template match="section"> 
    <xsl:variable name="count"> 
    <xsl:number/> 
    </xsl:variable> 
    <span class="title" id="title-{$count}"> 
    <xsl:value-of select="title"/> 
    </span> 
</xsl:template> 

Không chỉ là phương pháp này ít tiết (<xsl:apply-templates/> thay thế cả hai <xsl:for-each><xsl:call-template/>), nhưng nó cũng trở nên ngay lập tức rõ ràng những gì nút hiện tại là. Tất cả những gì bạn phải làm là nhìn vào thuộc tính match và bạn ngay lập tức biết rằng bạn đang xử lý một phần tử <section> và các thành phần <section> là những gì bạn đang đếm.

Để có giải thích ngắn gọn về cách các quy tắc mẫu (tức là <xsl:template> các phần tử có thuộc tính match) hoạt động, hãy xem "How XSLT Works".

+0

Cảm ơn bạn rất nhiều !! Bài đăng và câu trả lời này cực kỳ hữu ích – anpatel

+0

Bạn được chào đón! Thật vui khi bạn thấy nó hữu ích. –

+0

Xin lỗi, Evan, nhưng đây là một giải pháp rất kém hiệu quả (O (N^2)). Một giải pháp sử dụng tham số truyền qua có thể là O (N). Tất cả điều này nói về "verbosity" chỉ là này - verbosity và không đề cập đến một từ về hiệu quả. Bạn có thể làm cho câu trả lời này hữu ích hơn cho người đọc nếu bạn đề cập đến độ phức tạp của giải pháp được đề xuất và so sánh nó với các giải pháp có thể khác. Vì những lý do này, tôi coi câu trả lời này là loại hướng dẫn ánh sáng và không thực tế cho công việc sản xuất. –

2

biến được định vị cục bộ và chỉ đọc trong xslt.

+0

tôi nhìn thấy.Bạn có biết một cách tiếp cận tôi có thể thực hiện để đạt được những gì tôi sau? – Marcel

+0

Đầu tiên tôi sẽ nói bạn nên tránh sử dụng cấu trúc foreach và mẫu gọi. Đây là các câu lệnh thủ tục và XSLT là đệ quy. Do đó bạn nên suy nghĩ theo cách đệ quy thay vì thủ tục. Người dùng @Bewarned hiển thị là cách hợp lệ để tăng số lượt truy cập của bạn thông qua tham số. Sau đó, sử dụng tốt hơn mẫu áp dụng với tham số thêm 1 mỗi lần được gọi. Chỉ cần bình luận điều này nếu tôi không rõ ràng. – Luixv

8

Biến XSLT không thể thay đổi. Bạn sẽ chuyển giá trị từ mẫu này sang mẫu khác.

Nếu bạn đang sử dụng XSLT 2.0, bạn có thể có tham số và sử dụng đường hầm để truyền biến cho đúng mẫu.

Mẫu của bạn sẽ trông giống như thế này:

<xsl:template match="a"> 
<xsl:param name="count" select="0"> 
    <xsl:apply-templates> 
    <xsl:with-param select="$count+1"/> 
    </xsl:apply-templates> 
</xsl:template> 

Ngoài ra nhìn vào sử dụng generate-id() nếu bạn muốn tạo id.

+2

+1 cho tạo-id() cảm ơn! –

0

Chưa tự mình thử, nhưng bạn có thể thử và chuyển một tham số cho mẫu. Trong mẫu đầu tiên của bạn, bạn đặt tham số để đếm() (hoặc current() có thể?) Trong câu lệnh for-each và sau đó chuyển giá trị đó cho mẫu "section" của bạn.

Dưới đây là thêm về passing parameters to templates

2

Tùy thuộc vào bộ xử lý XSLT của bạn, bạn có thể giới thiệu chức năng kịch bản vào XLST của bạn. Ví dụ, thư viện Microsoft XML hỗ trợ việc đưa javascript vào. Xem http://msdn.microsoft.com/en-us/library/aa970889(VS.85).aspx để biết ví dụ. Chiến thuật này rõ ràng sẽ không hoạt động nếu bạn dự định triển khai/thực hiện XSLT trên các trình duyệt khách hàng công cộng; nó phải được thực hiện bởi một bộ xử lý XSLT cụ thể.

+0

Tôi đã sử dụng mẹo đó trước đây, nhưng nó chỉ nên được thực hiện như một phương sách cuối cùng, nơi mà việc cấu trúc nó dọc theo các dòng không thể thay đổi/chức năng sẽ bị cấm. Nhưng nó đã có tác dụng. Trong một số kịch bản (chẳng hạn như .NET), bạn có thể sử dụng các đối tượng mở rộng để làm điều tương tự bên ngoài xslt, nhưng một lần nữa: điều đó không làm cho nó trở thành một ý tưởng tuyệt vời. –

1

Bạn có thể sử dụng hàm position() để thực hiện những gì bạn muốn. Nó sẽ trông như thế này

<xsl:template match="/"> 
    <xsl:for-each select="section"> 
    <xsl:call-template name="section"> 
     <xsl:with-param name="counter" select="{position()}"/> 
    </xsl:call-template> 
    </xsl:for-each> 
</xsl:template> 

<xsl:template name="section"> 
    <xsl:param name="counter"/> 
    <span class="title" id="title-{$counter}"> 
    <xsl:value-of select="title"/> 
    </span> 
</xsl:template> 
+0

Thuộc tính select của xsl: with-param là một biểu thức, không phải là một chuỗi có thể sử dụng AVT. – jelovirt

+0

Ngoài ra, không cần phải chuyển giá trị của vị trí(), vì sẽ không thay đổi danh sách nút hiện tại. Bạn có thể dễ dàng truy cập cùng một giá trị, sử dụng position(), từ bên trong mẫu "section". –

6

Các biến trong XSLT là không thay đổi, do đó bạn phải phê duyệt vấn đề đó. Bạn có thể có thể sử dụng trực tiếp position():

<xsl:template match="/"> 
    <xsl:for-each select="section"> 
     <xsl:call-template name="section"/> 
    </xsl:for-each> 
</xsl:template> 

<xsl:template name="section"> 
    <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span> 
</xsl:template> 

Hoặc trong một khuôn mẫu định hướng nhiều cách:

<xsl:template match="/"> 
    <xsl:apply-templates select="section"/> 
</xsl:template> 

<xsl:template match="section"> 
    <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span> 
</xsl:template> 
0

Sử dụng <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" />$ ROWNUM như một giá trị incrementing.

Ví dụ: <xsl:template name="ME-homeTiles" match="Row[@Style='ME-homeTiles']" mode="itemstyle"> <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> ...<a href="{$SafeLinkUrl}" class="tile{$RowNum}"><img ....></a>

này sẽ tạo ra các lớp học cho liên kết với các giá trị tile1, tile2, tile3 vv ...

Các vấn đề liên quan