Một trong các khái niệm mới chủ yếu trong XPath 2.0 và XSLT 2.0 là tất cả mọi thứ là một dãy. Trong XPath 1.0 và XSLT 1.0, bạn thường làm việc với cây các nút. Tài liệu XML sau khi phân tích cú pháp là một cây có chứa nút document (tài liệu) và con cháu của nó. Nhờ sử dụng cây các nút, bạn có thể tìm thấy nút cho phần tử root (gốc), cùng với tất cả các con cháu, các thuộc tính, và các anh chị em của phần tử gốc đó. (Bất kỳ các nhận xét hoặc các lệnh xử lý nào bên ngoài phần tử gốc của tệp XML được coi là anh chị em của phần tử gốc này).
Khi bạn làm việc với một tài liệu XML trong XPath 2.0 và XSLT 2.0, bạn sử dụng dãy này theo cùng một cách như cấu trúc cây trong XPath 1.0 và XSLT 1.0. Dãy này chứa một mục duy nhất (nút tài liệu), và bạn sử dụng nó giống như cách bạn vẫn luôn áp dụng. Tuy nhiên, bạn có thể tạo ra các dãy các giá trị nguyên tử. Liệt kê 1, được lấy từ ứng dụng mẫu sắp tới, trong đó bạn sẽ quản lý dữ liệu cho một giải đấu đấu loại trực tiếp gồm 16 đội, cho thấy một ví dụ về một dãy các giá trị nguyên tử.
Liệt kê 1. Một dãy các giá trị nguyên tử
<xsl:variable name="seeds" as="xs:integer*">
<xsl:sequence
select="(1, 16, 8, 9, 5, 12, 4, 13, 6, 11, 3, 14, 7, 10, 2, 15)"/>
</xsl:variable> |
Mã này định nghĩa biến $seeds. Phần tử mới
<xsl:sequence> định nghĩa một dãy các
mục, như bạn có thể đoán ra. Trong trường hợp này, các mục có các kiểu
xs:integer của Lược đồ XML. Thuộc tính mới as
định nghĩa kiểu dữ liệu của biến, dấu hoa thị
(xs:integer*) có nghĩa là dãy này chứa từ không
đến nhiều số nguyên. Trong XPath 1.0 và XSLT 1.0, bạn sẽ tạo 16 nút văn
bản khác nhau và sau đó nhóm các nút đó lại thành một biến. Trong XPath
2.0 và XSLT 2.0, dãy hoạt động như là một mảng một chiều của các số, chính
là cái mà bạn muốn cho ứng dụng mẫu.
Các dãy tuân theo một vài quy tắc. Đầu tiên, chúng không thể chứa các dãy
khác. Nếu bạn tạo một dãy mới từ một dãy có ba mục, theo sau là một dãy
khác có ba mục, thì kết quả sẽ là một dãy mới có sáu mục. Thứ hai, các dãy
cho phép bạn pha trộn các nút và các mục. Bạn có thể tạo ra một dãy có
chứa các giá trị nguyên tử được hiển thị trong Liệt kê
1, cộng với tất cả các phần tử
<contestant>, như trong Liệt kê 2.
Liệt kê 2. Một dãy các nút và các giá trị nguyên tử
<xsl:variable name="seeds" as="item()*">
<xsl:for-each select="/bracket/contestants/contestant">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:sequence
select="(1, 16, 8, 9, 5, 12, 4, 13, 6, 11, 3, 14, 7, 10, 2, 15)"/>
</xsl:variable> |
Biến $seeds chứa tất cả các nút contestant (đội
tham gia thi đấu) và 16 giá trị nguyên tử mà bạn đã dùng trước đó. Lưu ý
rằng kiểu dữ liệu của biến đó là item()*. Một
item là một nút hoặc một giá trị nguyên tử, do đó, biến này có thể chứa
bất cứ cái gì.
Bây giờ bạn đã biết những điều cơ bản về các dãy và các mục, chúng ta hãy
xem xét toán tử to, là toán tử mới trong XPath
2.0 và XSLT 2.0. Nó cho phép bạn chọn một loạt các số nguyên. Ví dụ, bạn
có thể tạo một dãy như trong Liệt kê 3.
Liệt kê 3. Một dãy các số nguyên được tạo bằng toán tử
to <xsl:variable name="range" as="item()*"> <xsl:sequence select="1 to 16"/> </xsl:variable> |
Mã này tạo ra một biến có tên là $range chứa các
số nguyên từ 1 đến 16. Trong ứng dụng mẫu, bạn có thể sử dụng toán tử
to làm một cơ chế vòng lặp, như trong Liệt kê 4.
Liệt kê 4. Sử dụng toán tử
to để tạo vòng lặp<xsl:for-each select="1 to 32"> <!-- Do something useful here --> </xsl:for-each> |
Trước khi bạn xây dựng bản định kiểu, hãy xem xét ứng dụng mẫu chi tiết hơn.
Trong ứng dụng mẫu cho bài viết này, bạn sẽ quản lý dữ liệu cho một giải đấu đấu loại trực tiếp gồm 16 đội. Như bạn đã mong đợi, dữ liệu giải đấu được thể hiện bằng XML. Bạn sẽ tạo một bản định kiểu XSLT 2.0 để biến đổi dữ liệu XML thành một bảng HTML biểu thị các kết quả của giải đấu. Liệt kê 5 cho thấy định dạng tài liệu XML.
Liệt kê 5. Tài liệu XML có dữ liệu giải đấu
<?xml version="1.0" encoding="UTF-8"?>
<!-- tourney.xml -->
<bracket>
<title>RSDC Smackdown</title>
<contestants>
<contestant seed="1" image="images/homerSimpson.png">Donuts</contestant>
<contestant seed="2" image="images/caffeine.png">Caffeine</contestant>
<contestant seed="3" image="images/fearlessFreep.png">Fearless Freep</contestant>
<contestant seed="4" image="images/wmd.jpg">Weapons of Mass Destruction</contestant>
<contestant seed="5" image="images/haroldPie.jpg">Pie</contestant>
<contestant seed="6" image="images/adamAnt.png">Adam Ant</contestant>
<contestant seed="7" image="images/georgeWBush.jpg">Misunderestimated</contestant>
<contestant seed="8" image="images/sillyPutty.jpg">Silly Putty</contestant>
<contestant seed="9" image="images/krazyGlue.jpg">Krazy Glue</contestant>
<contestant seed="10" image="images/snoopDogg.png">Biz-Implification</contestant>
<contestant seed="11" image="images/atomAnt.png">Atom Ant</contestant>
<contestant seed="12" image="images/ajaxcan.png">AJAX</contestant>
<contestant seed="13" image="images/darthVader.jpg">Darth Vader</contestant>
<contestant seed="14" image="images/nastyCanasta.png">Nasty Canasta</contestant>
<contestant seed="15" image="images/jcp.png">Java Community Process</contestant>
<contestant seed="16" image="images/andre.png">Andre the Giant</contestant>
</contestants>
<results>
<result round="1" firstSeed="1" secondSeed="16" winnerSeed="1"/>
<result round="1" firstSeed="8" secondSeed="9" winnerSeed="9"/>
<result round="1" firstSeed="5" secondSeed="12" winnerSeed="5"/>
<result round="1" firstSeed="4" secondSeed="13" winnerSeed="4"/>
<result round="1" firstSeed="6" secondSeed="11" winnerSeed="11"/>
<result round="1" firstSeed="3" secondSeed="14" winnerSeed="3"/>
<result round="1" firstSeed="7" secondSeed="10" winnerSeed="10"/>
<result round="1" firstSeed="2" secondSeed="15" winnerSeed="2"/>
<result round="2" firstSeed="1" secondSeed="9" winnerSeed="1"/>
<result round="2" firstSeed="5" secondSeed="4" winnerSeed="5"/>
<result round="2" firstSeed="11" secondSeed="3" winnerSeed="3"/>
<result round="2" firstSeed="10" secondSeed="2" winnerSeed="2"/>
<result round="3" firstSeed="1" secondSeed="5" winnerSeed="1"/>
<result round="3" firstSeed="3" secondSeed="2" winnerSeed="2"/>
<result round="4" firstSeed="1" secondSeed="2" winnerSeed="2"/>
</results>
</bracket> |
Trong phần tử <title>, bạn có thể thấy tên
của giải đấu là RSDC Smackdown. Tài liệu này biểu diễn các kết quả thực tế
từ một phiên họp tại Hội nghị phát triển phần mềm Rational của IBM trong
năm nay.
Một khung bảng chứa 16 phần tử
<contestant> (đội tham gia thi đấu), mỗi
phần tử có tên (văn bản của nó), số hạt giống, và hình ảnh. Một giải đấu
16 đội có 15 cặp đấu. Mỗi cặp đấu được biểu diễn bằng một phần tử
<result>. Bốn dữ liệu gắn với mỗi cặp đấu
là: vòng đấu của trận đấu ấy, (thuộc tính
round), các số hạt giống của hai đội (được lưu
trữ trong các thuộc tính firstSeed và
secondSeed), và số hạt giống của đội thắng
(thuộc tính winnerSeed). Nhiệm vụ của bạn là
lấy tài liệu XML này và chuyển đổi nó thành một bảng HTML để minh họa các
kết quả, như hiển thị trong Hình 1.
Hình 1. Các kết quả giải đấu trong một bảng HTML
Bảng này có 32 hàng và 5 cột. Khi sử dụng cách tiếp cận XSLT 1.0, bạn có thể xây dựng mỗi lần một hàng của bảng HTML, như trong Liệt kê 6.
Liệt kê 6. Xây dựng một hàng của bảng HTML mỗi lần
<!-- Row 1 -->
<tr>
<td style="border: none; border-top: solid; border-right: solid;">
<xsl:text>[1] </xsl:text>
<xsl:value-of select="$contestants[@seed='1']/>
</td>
<td style="border: none;>
</td>
<td style="border: none;>
</td>
<td style="border: none;>
</td>
<td style="border: none;>
</td>
</tr>
<!-- Row 2 -->
. . . |
Cách này sẽ thực hiện được, nhưng bản định kiểu sẽ rất khó duy trì. Mã cho
mỗi hàng và cột được lặp lại trong suốt bản định kiểu. Vấn đề chính ở đây
là bạn cần 32 hàng trong bảng kết quả đầu ra. Mỗi một trong số 32 hàng
chứa dữ liệu từ một phần tử trong tài liệu XML (hoặc một
<contestant> hay một
<result>). Thật không may, bạn không có
32 phần tử để có thể lặp duyệt qua chúng. Bạn có thể sử dụng
<xsl:for-each
select="contestants/contestant|results/result">,
nhưng các phần tử đó không xuất hiện theo thứ tự mà bạn cần đến chúng
trong bảng. XSLT 1.0 không có nhiều công cụ để giúp bạn làm việc này.
Bạn có thể cấu trúc lại mã để làm cho bản định kiểu đơn giản hơn nhiều. Có các mẫu hình cho kiểu dáng và nội dung của các ô, và bạn có thể sử dụng các tính năng mới của XPath 2.0 và XSLT 2.0 để lặp duyệt qua các mẫu hình đó. Bản định kiểu cuối cùng là nhỏ hơn khoảng 70% so với phiên bản ban đầu (phải thừa nhận là vụng về). Trước khi bạn xây dựng bản định kiểu đó, chúng ta hãy xem xét cách cấu trúc lại mã.
Cấu trúc lại mã - Tính toán các kiểu dáng ô của bảng
Để cấu trúc lại mã, hãy bắt đầu di chuyển thông tin kiểu dáng sang một bản
định kiểu CSS. Như bạn có thể thấy trong Hình 2, bảng
khung HTML có 5 kiểu dáng ô khác nhau: None,
MatchupStart,
MatchupMiddle,
MatchupEnd, và
Solid.
Hình 2. Các kiểu dáng đường viền ô trong bảng HTML
Liệt kê 7 cho thấy mã CSS trông như thế nào.
Liệt kê 7. Bản định kiểu CSS
.None { width: 20%; }
.MatchupStart { width: 20%;
border: none; border-top: solid;
border-right: solid; }
.MatchupMiddle { width: 20%;
border: none; border-right: solid; }
.MatchupEnd { width: 20%;
border: none; border-bottom: solid;
border-right: solid; }
.Solid { width: 20%;
border: solid; } |
Các kiểu dáng đường viền làm cho việc xem kết quả của giải đấu thành dễ
dàng. Các đường ngang nối hai ô cho biết rằng hai đối thủ nào đã đối đầu
với nhau; ô có đường viền liền nét trong cột tiếp sau cho thấy người chiến
thắng của cặp đấu này. Nhìn vào các kiểu dáng đường viền, bạn có thể thấy
một mẫu hình xác định: Đối với mỗi cặp đối thủ, các ô trước đối thủ đầu
tiên không có đường viền (kiểu dáng là None),
các ô dành cho hai đối thủ có đường viền liền nét (kiểu dáng
Solid), các ô giữa hai đối thủ chỉ có đường
viền ở bên phải (kiểu dáng MatchupMiddle), và
các ô sau đối thủ cuối cùng không có đường viền (kiểu dáng
None). Điều này hơi khác một chút so với cột
đầu tiên. Vì các cặp đối thủ trong cột đầu tiên là quá gần nhau, nên ô
dành cho đối thủ đầu tiên có một đường viền trên đỉnh và bên phải (kiểu
dáng MatchupStart), và ô dành cho đối thủ thứ
hai có một đường viền dưới đáy và bên phải (kiểu dáng
MatchupEnd).
Lưu ý rằng các cặp đối thủ cách nhau xa hơn trong mỗi cột. Khoảng cách một ô giữa các đối thủ cột 1, khoảng cách ba ô ở cột 2, khoảng cách bảy ô ở cột 3, và khoảng cách 15 ô ở cột 4. Kích thước của mỗi khoảng cách bằng một lũy thừa hai trừ một, do đó, có một mẫu hình xác định ở đây.
Mẫu hình tổng quát xuyên suốt bảng này là mỗi cột có chứa một nhóm các ô lặp lại. Kích thước của các nhóm lặp lại (mà tôi sẽ gọi là chu kỳ của cột, vì thiếu một thuật ngữ hay hơn) là 4, 8, 16, 32 và 64 trong năm cột. Mỗi nhóm lặp lại có hai đối thủ, các ô giữa hai đối thủ và các ô trước và sau hai đối thủ.
Trong mỗi cột, hãy sử dụng hai giá trị trong tính toán của bạn:
$period, là kích thước của nhóm lặp lại, và
$oneQuarter, bằng 1/4 kích thước của chu kỳ.
(Việc lưu $period div 4 vào một biến làm cho mã
sạch hơn). Bảng 1 cho thấy các quy tắc tổng quát hóa
cho các kiểu dáng đường viền của ô.
Bảng 1. Các kiểu dáng đường viền của ô trong bảng HTML
| Công thức | Cột 1(chu kỳ 4) | Cột 2 (chu kỳ 8) | Cột 3 (chu kỳ 16) | Cột 4 (chu kỳ 32) | Cột 5 (chu kỳ 64) |
|---|---|---|---|---|---|
$row < $oneQuarter hoặc $row > $period -
$oneQuarter
| N/A | Hàng 1, kiểu dáng None | Các hàng 1-3, kiểu dáng
None | Các hàng 1-7, kiểu dáng
None | Các hàng 1-15, kiểu dáng
None |
$row = $oneQuarter
| Hàng 1, kiểu dáng
MatchupStart | Hàng 2, kiểu dáng Solid | Hàng 4, kiểu dáng Solid | Hàng 8, kiểu dáng Solid | Hàng 16, kiểu dáng Solid |
$row > $oneQuarter và $row < $period -
$oneQuarter
| Hàng 2, kiểu dáng
MatchupMiddle | Các hàng 3-5, kiểu dáng
MatchupMiddle | Các hàng 5-11, kiểu dáng
MatchupMiddle | Các hàng 9-23, kiểu dáng
MatchupMiddle | Các hàng 17-32, kiểu dáng
None |
$row = $period - $oneQuarter
| Hàng 3, kiểu dáng
MatchupEnd | Hàng 6, kiểu dáng Solid | Hàng 12, kiểu dáng Solid | Hàng 24, kiểu dáng Solid | N/A |
$row < $oneQuarter hoặc $row > $period -
$oneQuarter
| Hàng 4, kiểu dáng None | Các hàng 7-8, kiểu dáng
None | Các hàng 13-16, kiểu dáng
None | Các hàng 25-32, kiểu dáng
None | N/A |
Bây giờ bạn đã có một công thức đẹp đẽ để tìm ra kiểu dáng của bất kỳ ô cụ thể nào trong bảng. Cho trước số cột và số hàng, công thức làm việc như một phép màu.
Cấu trúc lại mã - Tính toán nội dung ô trong bảng
Khi bạn tạo ra một ô trong bảng, tất nhiên, bạn cũng cần phải đưa nội dung
thích hợp vào trong ô đó. Việc này cũng có một mẫu hình thú vị. Bất cứ ô
nào có kiểu dáng là None hoặc
MatchupMiddle đều không có bất kỳ nội dung nào.
Điều đó có nghĩa là bạn chỉ phải lo nghĩ về việc tìm kiếm các nội dung phù
hợp cho các ô có ba kiểu dáng khác còn lại.
Trong Bảng 1, bạn có thể thấy rằng các ô có nội dung
có thuộc tính
$row =
$oneQuarter hoặc $row = $period - $oneQuarter.
Với cột đầu tiên, bạn chỉ cần viết số hạt giống và tên của đội thi đấu
thích hợp. Các cặp đấu đều dựa vào các cách chọn hạt giống và mô tả các
cặp đấu được thể hiện trong Bảng 2.
Bảng 2. Các cặp đấu dựa vào cách chọn hạt giống
| [1] versus [16] |
| [8] versus [9] |
| [5] versus [12] |
| [4] versus [13] |
| [6] versus [11] |
| [3] versus [14] |
| [7] versus [10] |
| [2] versus [15] |
Các cặp đấu được bố trí sao cho nếu hạt giống xếp cao hơn luôn thắng, thì
đội xếp hạt giống cao nhất sẽ luôn đấu với đội xếp hạt giống thấp nhất còn
lại, đội xếp hạt giống thứ hai sẽ luôn đấu với đội xếp thứ hai từ dưới lên
còn lại, và v.v.. Nhìn vào các hạt giống theo thứ tự này (1, 16, 8, 9, 5,
12, 4, 13, 6, 11, 3, 14, 7, 10, 2, 15), bạn có thể thấy chúng phù hợp với
các giá trị trong $seeds. Các đối thủ được hiển
thị trong cột 1 theo thứ tự này.
Đối thủ xuất hiện trong tất cả các hàng khác, có nghĩa là hàng 1 có hạt
giống đầu tiên trong dãy, hàng 3 có hạt giống thứ hai trong dãy, hàng 5
hạt giống thứ ba trong dãy, và v.v.. Ở đây mẫu hình là
$row + 1 div 2 =
$index. Nói cách
khác, lấy số hàng, cộng 1, và chia cho 2 để nhận được chỉ số của hạt giống
trong dãy $seeds. Nội dung ô của bảng là số hạt
giống tiếp theo là tên của đối thủ có số hạt giống đó.
Mẫu hình trên bảo đảm nội dung cho cột 1. Với các cột từ 2 đến 5, như bạn
có thể dự kiến, sẽ phức tạp hơn. Thay vì xem xét các phần tử
<contestant>, bạn cần phải xem xét các
kết quả được hiển thị trong 15 phần tử
<result>.
Cột 2 chứa những đội thắng ở vòng 1. Điều đó có nghĩa là bạn cần phải xem
xét các phần tử <result> có thuộc tính
round="1". Để giữ cho mọi thứ đơn giản hơn, đội
thắng của cặp đấu đầu tiên (hạt giống 1 đấu với 16) được lưu trữ trong
phần tử <result> đầu tiên, đội thắng của
cặp đấu thứ hai (hạt giống 8 đấu với 9) được lưu trữ trong phần tử
<result> thứ hai , và v.v.. Những đội
thắng trong cột 2 được hiển thị trong các hàng 2, 6, 10, 14, 18, 22, 26,
và 30. Hãy tìm kiếm một mẫu hình, hàng 2 sử dụng phần tử
<result> đầu tiên, hàng 6 sử dụng phần tử
<result> thứ hai, và hàng 10 sử dụng phần tử <result> thứ ba. Với
cột này, nếu bạn lấy số hàng, cộng 2, và chia cho 4, bạn sẽ nhận được vị
trí của phần tử <result round="1"> phù
hợp.
Cột 3 và 4 được xử lý tương tự. Những đội thắng trong cột 3 được hiển thị
trong các hàng 4, 12, 20, và 28 (mẫu hình ở đây là cộng 4 và chia cho 8).
Những đội thắng trong cột 4 được hiển thị trong hàng 8 và 24 (cộng 8 và
chia cho 16). Cột 5 hiển thị một đội duy nhất — đội thắng
của toàn bộ giải đấu. Nếu bạn đang ở hàng 16, bạn sẽ hiển thị đội thắng từ
phần tử <result round="4">.
Cách cấu trúc lại để tìm vị trí của giá trị mà bạn tìm kiếm là
($row + $oneQuarter)
div ($oneQuarter * 2).
Việc tăng dần kích thước của mẫu hình lặp lại làm cho mã đơn giản. Với cột
1, vị trí tính được là chỉ số trong dãy các hạt giống; với các cột khác,
vị trí tính được là vị trí của phần tử
<result>.
Bây giờ bạn có một công thức đẹp đẽ để xác định cả nội dung lẫn kiểu dáng của mỗi ô trong bảng. Dựa vào số hàng và cột, bạn có thể tìm ra tất cả mọi thứ bạn cần biết.
Khai thác sức mạnh của XPath 2.0 và XSLT 2.0
Bây giờ bạn có thể sử dụng các kỹ thuật mới trong XPath 2.0 và XSLT 2.0 để
trau chuốt bản định kiểu. Để bắt đầu, hãy sử dụng toán tử
to. Nếu bạn dựng bảng bằng một ngôn ngữ lập
trình thủ tục, bạn có thể làm điều gì đó tương tự như Liệt kê 8.
Liệt kê 8. Cách tiếp cận thủ tục cho bảng định kiểu
for (int row=1; row<=32; row++)
for (int column=1; column<=5; column++)
// Build each cell in the table here |
Với XPath 2.0 và XSLT 2.0, bạn sử dụng
<xsl:for-each> để thay thế các vòng lặp
for mà bạn sử dụng trong một ngôn ngữ thủ tục.
Liệt kê 9 cho bạn biết cách làm như thế
nào.
Liệt kê 9. Thực hiện vòng lặp
for với toán tử to
<xsl:for-each select="1 to 32">
<xsl:variable name="outerIndex" select="."/>
<tr>
<xsl:for-each select="1 to 5"> |
Bạn cần phải giải quyết một vài tình tiết phức tạp ở đây. Đầu tiên, trong
XPath 1.0 và XSLT 1.0, việc sử dụng
<xsl:for-each> đã làm thay đổi ngữ cảnh
cho mỗi lần lặp. Ví dụ, nếu bạn sử dụng
<xsl:for-each
select="contestants/contestant>,
thì nút ngữ cảnh là <contestant> mới nhất
trong mỗi lần lặp. Khi bạn sử dụng toán tử to
để lặp duyệt qua các số nguyên khác nhau, mục ngữ cảnh (đó là ngữ cảnh
item trong 2.0) là không xác định. Như bạn có thể thấy
trong Liệt kê 9, bạn cần phải ghi lưu giá trị hiện
tại của <xsl:for-each> bên ngoài bởi vì
nó không có sẵn cho bạn ở <xsl:for-each>
bên trong.
Nhưng còn tệ hơn nữa. Nếu mục ngữ cảnh là không xác định, bạn không có
cách nào để chọn các nút từ tài liệu. Nếu bạn biết bạn đang ở hàng
1, cột 1, bạn có thể nhận được mục đầu tiên từ dãy
$seeds vì $seeds là
một biến chung. Điều đó nói cho bạn biết rằng bạn cần phải tìm phần tử
<contestant
seed="1">. Thật
không may, bạn không thể nhận được bất cứ điều gì trong tài liệu. Ngay cả
việc sử dụng một biểu thức XPath tuyệt đối như là
/bracket/contestants/contestant[@seed='1'] cũng
không thành công. Vì lý do đó, bạn cần lưu trữ các nút mà bạn quan tâm làm
các một biến chung. Liệt kê 10 cho bạn thấy cách
truy cập các một biến chung, bất cứ khi nào và ở bất cứ nơi nào bạn
cần.
Liệt kê 10. Một biến chung lưu trữ các nút bạn cần
<xsl:variable name="results" select="/bracket/results"/> <xsl:variable name="contestants" select="/bracket/contestants"/> |
Nếu bạn cần lấy tên của đội đấu hạt giống 16 , biểu thức XPath là
$contestants/contestant[@seed="16"]. Bạn truy
cập phần tử <result> tương tự; nếu bạn
cần nhận được đội thắng trong cặp đấu thứ hai trong vòng 2, biểu thức là
$results/result[@round="2"][2]/@winnerSeed. Liệt kê 11 cho thấy hai một biến chung nữa mà
bạn có thể sử dụng để tạo ra bảng HTML.
Liệt kê 11. Các một biến chung có ích khác
<xsl:variable name="periods" as="xs:integer*">
<xsl:sequence select="(4, 8, 16, 32, 64)"/>
</xsl:variable>
<xsl:variable name="backgroundColors" as="xs:string*">
<xsl:sequence
select="('background: #CCCCCC;', 'background: #9999CC;',
'background: #99CCCC;', 'background: #CC99CC;',
'background: #CCCC99;')"/>
</xsl:variable> |
Các biến này lưu trữ giá trị của chu kỳ cho mỗi cột và màu nền cho mỗi cột. Để sử dụng từng biến, bạn chỉ cần sử dụng số cột làm chỉ số của giá trị duy nhất mà bạn muốn.
Một cải tiến thú vị trong XPath 2.0 và XSLT 2.0 là phần tử
<xsl:function>. Hãy tạo hai hàm trong bản
định kiểu là: cellStyle và
getResults. Hàm đầu tiên trả về kiểu dáng đường
viền cho mỗi ô, trong khi hàm thứ hai trả về kết quả (nếu có) cho một cặp
đấu. Các tham số cho cả hai hàm là số hàng và cột của ô. Liệt kê 12 trình bày mã cho hàm
cellStyle.
Liệt kê 12. Hàm
cellStyle
<xsl:function name="bracket:cellStyle" as="xs:string">
<xsl:param name="row" as="xs:integer"/>
<xsl:param name="column" as="xs:integer"/>
<xsl:variable name="period" as="xs:integer"
select="subsequence($periods, $column, 1)"/>
<xsl:variable name="oneQuarter" as="xs:integer"
select="$period div 4"/>
<xsl:variable name="lastColumn" as="xs:boolean"
select="$oneQuarter = count($contestants/contestant)"/>
<xsl:variable name="position" select="$row mod $period"/>
<xsl:value-of
select="if ($position = $oneQuarter) then
(if ($column = 1) then 'MatchupStart'
else 'Solid')
else if ($position = $period - $oneQuarter) then
(if ($column = 1) then 'MatchupEnd'
else 'Solid')
else if ($lastColumn) then 'None'
else if ($position < $oneQuarter or
$position > $period - $oneQuarter) then 'None'
else 'MatchupMiddle'"/>
</xsl:function> |
Trước khi bạn xác định kiểu dáng ô, bạn sử dụng 4 biến. Bạn lấy ra giá trị
$period từ một biến chung
$periods. Như tôi đã đề cập ở trên,
$oneQuarter đơn giản là
$period div
4. Biến
$lastColumn kiểu boolean là kết quả so sánh
$oneQuarter với tổng số đội trong giải đấu. Nếu
các giá trị đó bằng nhau, bạn đang xử lý cột cuối cùng. Cuối cùng, biến
$position cho biết vị trí của hàng hiện tại
trong mẫu hình. Nói cách khác, hàng 9 trong bảng là hàng đầu tiên của nhóm
lặp lại cho các cột 1 và 2. Hãy sử dụng vị trí của hàng trong mẫu hình để
tìm ra kiểu dáng của ô.
XPath 2.0 và XSLT 2.0 có một toán tử if, gồm một
biểu thức (trong ngoặc), tiếp sau là mệnh đề
then và mệnh đề
else. Toàn bộ các thành phần này nằm trong
thuộc tính select của
<xsl:value-of>. Trong ví dụ này, bạn thay
thế phần tử <xsl:choose> dài dòng hơn
nhiều bằng chỉ một biểu thức. Mã ở đây là khá đơn giản, dựa vào cuộc thảo
luận về các công thức bảng; nếu logic phức tạp hơn, thì có lẽ sử dụng
<xsl:choose>, sẽ dễ bảo trì hơn, mặc dù
nó đòi hỏi phải gõ bàn phím nhiều hơn.
Một số lưu ý về cú pháp cho hàm
<xsl:function>: Đầu tiên, bạn cần phải
khai báo một vùng tên mới cho các hàm này. Nếu bạn gọi
bracket:cellStyle() trong một biểu thức XPath,
vùng tên này sẽ cho bộ xử lý XSLT 2.0 biết cách tìm hàm này. Thứ hai, lưu
ý rằng bạn đã sử dụng thuộc tính as="xs:string"
để chỉ ra rằng hàm này trả về một chuỗi. Phần tử
<xsl:value-of> trả về một trong năm tên
kiểu dáng, đó là đầu ra của hàm.
Hàm getResults có phức tạp hơn một chút, nhưng
không nhiều, bạn có thể thấy trong Liệt kê
13.
Liệt kê 13. Hàm
getResults
<xsl:function name="bracket:getResults" as="xs:string">
<xsl:param name="row" as="xs:integer"/>
<xsl:param name="column" as="xs:integer"/>
<xsl:variable name="period" as="xs:integer"
select="subsequence($periods, $column, 1)"/>
<xsl:variable name="oneQuarter" as="xs:integer"
select="$period div 4"/>
<xsl:variable name="position" select="$row mod $period"/>
<xsl:choose>
<xsl:when test="$position = $oneQuarter
or
$position = $period - $oneQuarter">
<xsl:variable name="round" select="$column - 1"/>
<xsl:variable name="index"
select="($row + $oneQuarter) div ($oneQuarter * 2)"/>
<xsl:variable name="currentSeed"
select="if ($column = 1) then
subsequence($seeds, $index, 1)
else
$results/result[@round=$round][$index]/@winnerSeed"/>
<xsl:choose>
<xsl:when test="string-length(string($currentSeed))">
<xsl:value-of
select="concat('[', $currentSeed, '] ',
$contestants/contestant[@seed=$currentSeed])"/>
</xsl:when>
<xsl:otherwise>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:function> |
Bạn có các tham số tương tự như trước đây. Bạn phải làm một số việc xử lý
đặc biệt ở đây cho cột 1, có chứa dữ liệu từ các phần tử
<contestant>, trong khi các cột khác chứa
dữ liệu từ các phần tử <result>. Cũng
giống như với hàm cellStyle bạn tính toán
$period và
$position, và bạn sử dụng biến
$oneQuarter để đơn giản hóa các công thức.
Nếu ô chứa một đội thi đấu, bạn tính toán thêm ba biến. Biến
$round là số cột trừ đi một (cột 2 chứa kết quả
của vòng 1), và bạn tính toán biến $index theo
công thức thảo luận ở trên.
Bạn cần phải làm theo hai bước để tìm dữ liệu thích hợp. Đầu tiên, thiết
lập giá trị của biến $currentSeed. Nếu đây là
vòng 1, bạn sử dụng hàm subsequence mới để chọn
một giá trị từ biến $seeds. Đối với các vòng
khác, bạn lấy thuộc tính winnerSeed từ phần tử
<result> thích hợp.
Thứ hai, ngoài việc xử lý cột 1 khác đi, bạn cần tính đến khả năng có một
phần tử <result> không có đội thắng
(winnerSeed=""). Nếu điều đó xảy ra, hãy trả về
một khoảng trống cứng ( ). Theo cách
XSLT 2.0 xử lý các kiểu dữ liệu (đây là một chủ đề cho một bài viết sắp
tới), bạn cần phải chuyển đổi $currentSeed
thành một chuỗi, rồi kiểm tra độ dài của chuỗi. Nếu chiều dài của chuỗi
bằng không, hãy trả về một khoảng trống cứng; ngược lại, hãy trả về hạt
giống và tên của đội thi đấu.
Cuối cùng, lưu ý rằng bạn sử dụng hàm
<xsl:choose> ở đây. Mặc dù bạn có thể làm
mọi thứ với chỉ một câu lệnh if của XPath, mã
sẽ rất cồng kềnh. Mặc dù hàm <xsl:choose>
dài dòng hơn, mã sạch hơn và dễ hiểu hơn.
Bây giờ khi bạn đã thiết lập các một biến chung và các hàm mà bạn cần, trọng tâm của bản kiểu định kiểu là đơn giản và đẹp đẽ, bạn có thể thấy trong Liệt kê 14.
Liệt kê 14. Trọng tâm của bản định kiểu
<xsl:for-each select="1 to 32">
<xsl:variable name="outerIndex" select="."/>
<tr>
<xsl:for-each select="1 to 5">
<td style="{subsequence($backgroundColors, ., 1)}"
class="{bracket:cellStyle($outerIndex, .)}">
<xsl:value-of
select="bracket:getResults($outerIndex, .)"/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each> |
Bạn có hai vòng lặp tạo ra 5 cột với 32 hàng của bảng. Đối với mỗi ô, màu
nền được dựa vào số cột hiện tại. Hàm cellStyle
xác định kiểu dáng đường viền (class="x"), và
hàm getResults xác định giá trị của ô.
Như bạn đã có thể dự kiến, cần một trình xử lý XSLT 2.0 để sử dụng bản định
kiểu này. Tôi sẽ không in lại toàn bộ bản định kiểu ở đây, nhưng bạn phải
sử dụng <xsl:stylesheet version="2.0"> để
cho trình xử lý sẽ chạy ở chế độ XSLT 2.0. Tôi rất khuyến cáo nên dùng
trình xử lý Saxon của Michael Kay (xem phần Tài
nguyên để biết thêm chi tiết). Tiến sĩ Kay vốn là người soạn thảo
đặc tả XSLT 2.0, và Saxon, theo một vài phương diện, đã là một ca kiểm thử
khi đặc tả này đang được phát triển. Nếu bạn sử dụng Saxon, hãy dùng lệnh
dưới đây để biến đổi tệp XML tourney.xml bằng
cách sử dụng bản định kiểu results-html.xsl và
viết đầu ra cho tệp results.html:
java net.sf.saxon.Transform -o results.html tourney.xml
results-html.xsl
Để minh họa cho tính linh hoạt của bản định kiểu này, hãy lấy ra một vài
kết quả trong tệp XML và chạy phép biến đổi này. Liệt
kê 15 cho thấy phần tử <result>
trông như thế nào.
Liệt kê 15. Tài liệu XML với dữ liệu giải đấu chưa hoàn thành
<?xml version="1.0" encoding="UTF-8"?>
<!-- incomplete-tourney.xml -->
<bracket>
. . .
<results>
<result round="1" firstSeed="1" secondSeed="16" winnerSeed="1"/>
<result round="1" firstSeed="8" secondSeed="9" winnerSeed="9"/>
<result round="1" firstSeed="5" secondSeed="12" winnerSeed="5"/>
<result round="1" firstSeed="4" secondSeed="13" winnerSeed="4"/>
<result round="1" firstSeed="6" secondSeed="11" winnerSeed="11"/>
<result round="1" firstSeed="3" secondSeed="14" winnerSeed="3"/>
<result round="1" firstSeed="7" secondSeed="10" winnerSeed="10"/>
<result round="1" firstSeed="2" secondSeed="15" winnerSeed="2"/>
<result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="3" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="3" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="4" firstSeed="" secondSeed="" winnerSeed=""/>
</results>
</bracket> |
Liệt kê 15 biểu diễn trạng thái của giải đấu sau
vòng đầu tiên. Tất cả các phần tử
<result> cho vòng 2, 3, và 4 có một thuộc
tính trống winnerSeed. Mặc dù vậy, bản định
kiểu tạo ra khung bảng chính xác, bạn có thể thấy trong Hình 3.
Hình 3. Khung bảng cho một giải đấu chưa hoàn thành
Bài viết này đã trình diễn nhiều tính năng mới của XPath 2.0 và XSLT 2.0.
Trong ứng dụng mẫu, bạn đã chọn một bản định kiểu cồng kềnh và đã cấu trúc
lại nó thành một đoạn mã nhỏ hơn nhiều và dễ bào trì hơn. Một phần nỗ lực
của bạn là để phân tích mã nhằm tìm ra các mẫu hình, nhưng nếu không có
toán tử to, hàm
<xsl:function>, và dãy các mục, thì việc
sắp xếp hợp lý bảng định kiểu nhiều như bạn đã làm sẽ rất khó khăn.
Đáng kể nhất, bạn đã tạo ra các hàm chung có thể xử lý bất kỳ số lượng đối
thủ nào. Ví dụ, để thay đổi bản định kiểu nhằm xử lý một giải đấu có 32
đội, bạn cần phải thay đổi các dãy $seeds và
$periods. Bạn cũng cần thay thế
select="1 to
5" bằng
select="1 to $rounds", ở đây
$rounds là số vòng thi đấu trong giải đấu. Tất
nhiên, giải pháp đẹp đẽ nhất là tạo ra hàm XSLT 2.0 tính toán các giá trị
cho bất kỳ khung bảng chung nào, bao gồm cả số vòng đấu, dãy các hạt giống
(một khung bảng 32-đội mô tả các cặp đấu giữa 1 và 32, 2 và 31, và v.v..),
và các chu kỳ cho các cột khác nhau. Thách thức này được để dành làm một
bài tập cho người đọc.
Cốt lõi của vấn đề là bạn cần lặp duyệt qua 32 hàng của bảng, nhưng XSLT 1.0 không cung cấp cho bạn cách nào thiết thực nào để làm điều đó. Các tính năng mới của XPath 2.0 và XSLT 2.0 giúp bạn giải quyết vấn đề này.
| Mô tả | Tên | Kích thước | Phương thức tải |
|---|---|---|---|
| XML, XSLT, and HTML code samples | x-xslt20xpath20/samples.zip | 12KB | HTTP |
Học tập
- XSLT
là loại
ngôn ngữ gì? (Michael Kay, developerWorks, 04. 2005): Hãy đặt ngôn
ngữ XSLT trong bối cảnh với bài viết này về triết lý thiết kế đằng sau nó,
nó xuất phát từ đâu, nó dùng để làm gì, và tại sao lại sử dụng
nó.
- Kế hoạch nâng cấp XSLT 1.0 lên 2.0 (David Marston, Joanne Tong,
và Henry Zongaro; developerWorks) Trong loạt bài này, hãy tìm hiểu cách di
chuyển các bản định kiểu của bạn từ XSLT 1.0 lên tiêu chuẩn
mới.
- Trang Họ ngôn ngữ bản định kiểu mở rộng: Tìm tất cả các khuyến
nghị mới cho XSLT 2.0, XPath 2.0, XQuery 1.0, và các đặc tả khác đã trở
thành khuyến nghị chính thức của W3C (World Wide Web Consortium) vào ngày
23.01.2007. Toàn bộ định nghĩa ngôn ngữ bao gồm một số đặc tả.
- Chứng chỉ XML
của IBM: Tìm hiểu cách bạn có thể trở thành một nhà phát triển có
chứng chỉ của IBM về XML và các công nghệ liên quan.
- Thư
viện kỹ thuật XML: Xem Vùng XML của developerWorks để thấy một
vùng rộng lớn các bài viết kỹ thuật và các lời khuyên, các hướng dẫn, các
tiêu chuẩn, và các sách Đỏ của IBM.
- Các sự kiện kỹ thuật và Webcast của developerWorks: Theo sát với
công nghệ trong các phiên này.
- Kho sách kỹ thuật: Duyệt qua các sách về chủ đề này và các chủ đề
khác.
Lấy sản phẩm và công nghệ
- Trình xử lý Saxon XSLT
2.0: Tìm trình xử lý của Michael Kay trên SourceForge.
- Trang XML của Altovatrên
trang web Altova: Nhận thông tin và dùng thử trình xử lý Altova
XSLT. Altova, nhà sản xuất XMLSpy và các sản phẩm phổ biến khác, cung cấp
bộ xử lý XSLT của mình có sẵn miễn phí. Nó hỗ trợ XSLT 1.0, XSLT 2.0, và
XQuery 1.0. Mặc dù nó không phải mã nguồn mở, nhưng giấy phép của nó cho
phép bạn nhúng máy XSLT vào trong các sản phẩm của bạn.
- Phần mềm dùng thử IBM: Xây dựng dự án phát triển tiếp theo của
bạn với phần mềm dùng thử có sẵn để tải trực tiếp từ
developerWorks.
Thảo luận
- Các
diễn đàn thảo luận lĩnh vực XML: Tham gia vào bất kỳ các cuộc thảo
luận nào liên quan đến XML.
- Các blog
developerWorks: Đọc các blog này và dành thời gian cho cộng đồng
developerWorks.
Lập trình viên cao cấp Doug Tidwell là người quảng bá các dịch vụ Web cho IBM. Ông từng là phát ngôn viên ở hội nghị về XML đầu tiên vào năm 1997, có kinh nghiệm nghiên cứu về ngôn ngữ đánh dấu trong hơn một thập kỷ. Ông có một bằng cử nhân tiếng Anh của trường đại học Georgia và một bằng thạc sỹ về Khoa học máy tính từ trường Đại học Vanderbilt