2013-01-15 42 views
6

Tôi khá mới đối với XSLT và do đó tôi muốn biết thực tiễn tốt nhất để kiểm tra sự tồn tại của một thuộc tính là gì. XML của tôi trông giống như sau:XSLT nếu thuộc tính tồn tại/else

<root> 
    <languages> 
     <lang id="EN">English<lang> 
     <lang id="FR">French<lang> 
     <lang id="DE">German</lang> 
    </languages> 
    <items> 
     <item lang="EN">test 1</item> 
     <item>test 2</item> 
     <item lang="FR">item 3</item> 
    </items> 
</root> 

Lưu ý rằng thuộc tính 'lang'-cho mục' là tùy chọn.

Bây giờ tôi muốn lặp qua các mục bằng cách sử dụng vòng lặp, trong khi kiểm tra xem nó có thuộc tính "lang" hay không. Nếu có, tôi muốn lấy toàn bộ chuỗi bằng cách sử dụng ID (ví dụ: EN -> 'English'). Nếu thuộc tính không được thiết lập, tôi muốn nó được viết là "No language set" hoặc một cái gì đó giống nhau.

Bây giờ tôi sử dụng mã sau đây nhưng tôi tự hỏi bản thân mình nếu nó không thể được thực hiện một cách hiệu quả hơn.

<xsl:for-each select="//root/items/item"> 
    <xsl:variable name="cur_lang" select="@lang" /> <!-- first I store the attr lang in a variable --> 
    <xsl:choose> 
     <xsl:when test="@lang"> <!-- then i test if the attr exists --> 
      <xsl:value-of select="//root/languages/lang[@id=$cur_lang]" /> <!-- if so, parse the element value --> 
     </xsl:when> 
     <xsl:otherwise> 
      No language set <!-- else --> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:for-each> 

Bất kỳ đề xuất/mẹo nào?

+2

Bạn có thể thêm một ví dụ về những gì đầu ra chính xác nên được đưa ra đầu vào bạn đã cung cấp? –

+0

Carlo, Bạn quên cung cấp kết quả mong muốn - Tôi nghĩ rằng bạn muốn chuỗi ngôn ngữ mở rộng thay thế giá trị thuộc tính 'lang' hiện tại. Vui lòng * chỉnh sửa * câu hỏi và cung cấp kết quả mong muốn khi chuyển đổi được áp dụng trên tài liệu XML được cung cấp. –

Trả lời

7

Việc sử dụng khóa có thể hiệu quả hơn. Bạn xác định một chìa khóa bằng phần tử cấp cao nhất bên ngoài các mẫu của bạn

<xsl:key name="langByCode" match="lang" use="@id" /> 

Sau đó, trong vòng lặp bạn chỉ có thể nói

<xsl:when test="@lang"> <!-- then i test if the attr exists --> 
    <xsl:value-of select="key('langByCode', @lang)" /> 
</xsl:when> 

Nhưng nói chung một cách tiếp cận XSLT tự nhiên hơn để toàn bộ điều sẽ đến sử dụng mẫu phù hợp thay vì for-eachif:

<xsl:template match="item[@lang]"> 
    <xsl:value-of select="key('langByCode', @lang)" /> 
</xsl:template> 

<xsl:template match="item"> 
    <xsl:text>No language set</xsl:text> 
</xsl:template> 

Với những mẫu tại chỗ sau đó bạn có thể làm <xsl:apply-templates select="/root/items/item" /> và nó sẽ tự động chọn mẫu thích hợp cho mỗi mục. Quy tắc là nó sẽ sử dụng mẫu cụ thể nhất, do đó, item[@lang] một cho những mục có thuộc tính lang và đồng bằng item thuộc tính cho những mục không có.

Một khả năng thứ ba là một mẹo nhỏ tôi đã học trên SO để đưa toàn bộ if/else kiểm tra vào một biểu thức XPath đơn

<xsl:value-of select=" 
    substring(
    concat('No language set', key('langByCode', @lang)), 
    1 + (15 * boolean(@lang)) 
)" /> 

Bí quyết ở đây là boolean(@lang) khi đối xử như một số là 1 nếu thuộc tính lang tồn tại và 0 nếu không. Nếu có một lang="EN", giả sử, sau đó chúng tôi xây dựng một chuỗi "No language setEnglish" và sau đó lấy chuỗi con bắt đầu từ ký tự thứ 16, là "English". Nếu có thuộc tính là no lang chúng tôi tạo chuỗi "No language set" (vì giá trị chuỗi của tập hợp nút trống là chuỗi trống) và lấy chuỗi con bắt đầu từ ký tự đầu tiên (nghĩa là toàn bộ chuỗi).

Bạn có thể sử dụng cùng một mẹo với các thuộc tính khác, ví dụ: giả sử chúng ta có một thuộc tính màu tùy chọn và muốn nói "No color specified" nếu nó vắng mặt, bạn có thể làm điều đó với

<xsl:value-of select="substring(
    concat('No color specified', @color), 
    1 + (18 * boolean(@color)) 
)" /> 
+0

Cảm ơn, tôi cố gắng sử dụng nhưng khi tôi làm điều này, trang không tải. Tôi có thể làm gì sai? – carlo

+0

Giải quyết vấn đề bằng cách di chuyển ra khỏi carlo

+0

Bây giờ tôi có thể làm gì nếu có thêm thuộc tính tùy chọn cho , giả sử 'màu'? – carlo

0

Một lựa chọn khác, nếu bạn có thể sử dụng XSLT 3.0, là một (một liên kết hữu ích map: map).

XML Input (cố định là tốt được hình thành)

<root> 
    <languages> 
     <lang id="EN">English</lang> 
     <lang id="FR">French</lang> 
     <lang id="DE">German</lang> 
    </languages> 
    <items> 
     <item lang="EN">test 1</item> 
     <item>test 2</item> 
     <item lang="FR">item 3</item> 
    </items> 
</root> 

XSLT 3,0

<xsl:stylesheet version="3.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:map="http://www.w3.org/2005/xpath-functions/map" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" extension-element-prefixes="xs map"> 
    <xsl:output indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:variable name="lang-map" as="map(xs:string, xs:string)" 
     select="map:new(
     for $lang in /*/languages/lang 
     return 
      map{$lang/@id := $lang/string()} 
     )"/> 

    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="languages"/> 

    <xsl:template match="item[@lang and map:contains($lang-map,@lang)]"> 
     <item><xsl:value-of select="$lang-map(current()/@lang)"/></item> 
    </xsl:template> 

    <xsl:template match="item"> 
     <item>No language found.</item> 
    </xsl:template> 

</xsl:stylesheet> 

Output

<root> 
    <items> 
     <item>English</item> 
     <item>No language found.</item> 
     <item>French</item> 
    </items> 
</root> 
Các vấn đề liên quan