1 00:00:00,430 --> 00:00:01,920 In this video we'll introduce querying 2 00:00:02,380 --> 00:00:03,610 XML data using XSLT. 3 00:00:04,360 --> 00:00:06,990 As a reminder, querying 4 00:00:07,690 --> 00:00:08,570 XML data is not nearly 5 00:00:08,890 --> 00:00:10,200 as mature as querying relational data 6 00:00:10,840 --> 00:00:12,040 due to it being much newer and 7 00:00:12,220 --> 00:00:14,920 not having a nice underlying algebra like the relational algebra. 8 00:00:16,010 --> 00:00:17,430 We already talked about XPath 9 00:00:17,530 --> 00:00:18,910 which was the first language developed for 10 00:00:19,040 --> 00:00:21,160 querying XML data. 11 00:00:21,630 --> 00:00:22,800 And we've also talked about 12 00:00:22,990 --> 00:00:24,920 XQuery which was actually developed after 13 00:00:25,510 --> 00:00:27,020 XSLT, but it's similar 14 00:00:27,490 --> 00:00:29,080 to XPath in it's style, where 15 00:00:29,310 --> 00:00:30,580 XSLT, which we're going 16 00:00:30,760 --> 00:00:33,000 to cover in this video, is actually quite different. 17 00:00:34,470 --> 00:00:35,760 XSL stands for the 18 00:00:35,820 --> 00:00:38,350 Extensible Stylesheet Language, and 19 00:00:38,470 --> 00:00:40,100 it was introduced originally but soon 20 00:00:40,390 --> 00:00:42,750 extended to included transformations and 21 00:00:42,910 --> 00:00:44,390 XSLT is currently much 22 00:00:44,640 --> 00:00:46,190 more widely used than XSL. 23 00:00:47,860 --> 00:00:49,970 Here's how we can think of XSLT as a query language. 24 00:00:50,950 --> 00:00:52,870 We have an XSLT processor and 25 00:00:53,080 --> 00:00:54,000 we feed to that processor 26 00:00:54,840 --> 00:00:56,140 our XML data in the 27 00:00:56,230 --> 00:00:57,940 form of a document or a stream. 28 00:00:58,710 --> 00:00:59,630 And we also give the processor 29 00:01:00,260 --> 00:01:02,480 a specification in XSLT, which 30 00:01:02,680 --> 00:01:05,020 by the way is expressed, using the XML format. 31 00:01:06,050 --> 00:01:07,190 The processor takes the data 32 00:01:07,520 --> 00:01:09,160 and the specification, and it 33 00:01:09,390 --> 00:01:10,970 transforms the data into 34 00:01:11,220 --> 00:01:12,290 a result, which is also 35 00:01:12,620 --> 00:01:14,460 expressed as an XML document or string. 36 00:01:15,810 --> 00:01:16,900 Now if we think about traditional 37 00:01:17,500 --> 00:01:20,480 database query processing, there's actually a natural mapping. 38 00:01:21,600 --> 00:01:22,590 If we think even about relational 39 00:01:23,280 --> 00:01:25,670 processing, we have a query processor and a database. 40 00:01:26,260 --> 00:01:27,120 We feed the data to the 41 00:01:27,220 --> 00:01:28,790 query processor, we feed 42 00:01:28,940 --> 00:01:29,870 the query to the query processor 43 00:01:30,520 --> 00:01:32,150 as well, and out comes the answer. 44 00:01:33,030 --> 00:01:35,000 So XSLT processing, although it 45 00:01:35,110 --> 00:01:37,480 really is through transformations, it can 46 00:01:37,690 --> 00:01:39,490 be thought of very much like querying a database. 47 00:01:40,430 --> 00:01:41,940 So even though XSLT be thought 48 00:01:42,190 --> 00:01:43,790 of as a query language, the query 49 00:01:44,160 --> 00:01:45,720 paradigm itself is quite different 50 00:01:46,200 --> 00:01:47,760 from what we're used to with SQL 51 00:01:48,230 --> 00:01:49,910 or even with XPath or XQuery. 52 00:01:50,590 --> 00:01:52,060 It's based fundamentally on the 53 00:01:52,140 --> 00:01:53,640 notion of transforming the data. 54 00:01:54,360 --> 00:01:56,150 And that transformation occurs with rules. 55 00:01:57,500 --> 00:01:58,960 To understand what the rules 56 00:01:59,340 --> 00:02:00,930 do and how the transformations work, it's 57 00:02:01,050 --> 00:02:03,550 again very instructive to think of the XML as a tree. 58 00:02:04,220 --> 00:02:05,480 So let's take our bookstore 59 00:02:05,960 --> 00:02:07,160 data and again make it 60 00:02:07,240 --> 00:02:08,370 a tree as we did before 61 00:02:08,770 --> 00:02:10,270 when we were first learning about XPath. 62 00:02:10,640 --> 00:02:11,750 So we have some books sub-elements 63 00:02:12,220 --> 00:02:13,710 and we have a 64 00:02:14,130 --> 00:02:16,340 magazine sub-element and I 65 00:02:16,510 --> 00:02:18,220 won't be elaborating all of these. 66 00:02:18,810 --> 00:02:20,830 We'll just imagine sub-trees here with 67 00:02:21,010 --> 00:02:22,600 our book we have a title 68 00:02:23,640 --> 00:02:25,770 and we have some authors. 69 00:02:27,600 --> 00:02:28,950 The title might be our 70 00:02:29,170 --> 00:02:30,330 leaf so we'll have a first course in 71 00:02:30,430 --> 00:02:32,440 database systems for example, whereas 72 00:02:32,710 --> 00:02:33,920 our authors may have author 73 00:02:34,330 --> 00:02:36,050 sub-elements, and within 74 00:02:36,430 --> 00:02:38,490 those author sub-elements we might 75 00:02:38,740 --> 00:02:39,700 have first name name and 76 00:02:39,880 --> 00:02:42,720 last name, abbreviated here, 77 00:02:43,250 --> 00:02:44,520 with string values for those, 78 00:02:45,730 --> 00:02:47,930 and of course more authors sub-elements as well. 79 00:02:48,160 --> 00:02:50,090 So that give the basic 80 00:02:50,440 --> 00:02:51,660 idea of a tree structure 81 00:02:52,160 --> 00:02:53,910 of XML, exactly as we've seen before. 82 00:02:54,740 --> 00:02:55,670 So now let's see what happens 83 00:02:56,020 --> 00:02:58,390 with XSLT in light of this tree structure. 84 00:02:59,480 --> 00:03:00,610 So the first thing that 85 00:03:00,750 --> 00:03:01,820 we have is the concept of 86 00:03:01,920 --> 00:03:04,610 matching a template and replacing it. 87 00:03:04,680 --> 00:03:06,140 So the idea in XSLT is that 88 00:03:06,280 --> 00:03:08,170 we can write an expression that finds 89 00:03:08,400 --> 00:03:09,940 a template that finds portion 90 00:03:10,400 --> 00:03:12,550 of the XML tree based on template matching. 91 00:03:13,060 --> 00:03:14,500 For example we might find 92 00:03:14,850 --> 00:03:15,910 books that have certain authors 93 00:03:16,940 --> 00:03:18,390 and once we find those will 94 00:03:18,530 --> 00:03:19,820 actually replace the entire 95 00:03:20,300 --> 00:03:23,350 subtree with the result of what we put in our template. 96 00:03:23,900 --> 00:03:24,920 For example, we might decide 97 00:03:25,350 --> 00:03:26,120 that want to pick the title 98 00:03:26,570 --> 00:03:29,010 here and replace this entire subtree with the title. 99 00:03:29,630 --> 00:03:30,960 Or we might match down 100 00:03:31,240 --> 00:03:32,470 to our authors and we might 101 00:03:32,750 --> 00:03:33,850 find our first name and last 102 00:03:34,030 --> 00:03:35,490 name, and say replace this entire 103 00:03:35,890 --> 00:03:37,090 author sub-element with the 104 00:03:37,150 --> 00:03:38,890 concatenation of the first and last name. 105 00:03:39,580 --> 00:03:41,070 Again the idea being that you 106 00:03:41,290 --> 00:03:42,870 write templates that match within 107 00:03:43,120 --> 00:03:44,320 the tree, using, in fact, XPath 108 00:03:45,000 --> 00:03:45,910 as we'll see as one of 109 00:03:46,000 --> 00:03:47,220 the portions of writing those 110 00:03:47,430 --> 00:03:49,790 templates, and then replace that portion of the tree. 111 00:03:51,300 --> 00:03:52,170 We can also do that recursively. 112 00:03:53,200 --> 00:03:54,600 So we can, for example, decide 113 00:03:54,930 --> 00:03:56,110 that we're going to replace this 114 00:03:56,360 --> 00:03:57,950 book with a different 115 00:03:58,460 --> 00:04:01,720 element and then recursively apply our templates to its children. 116 00:04:02,530 --> 00:04:03,210 We'll see that in a demo. 117 00:04:03,780 --> 00:04:05,130 It takes a little getting used to again. 118 00:04:05,530 --> 00:04:07,500 The XSLT language has 119 00:04:07,720 --> 00:04:09,740 the ability to extract values, and 120 00:04:09,840 --> 00:04:10,980 again it often uses XPath 121 00:04:11,650 --> 00:04:13,060 expressions in order to do that. 122 00:04:14,140 --> 00:04:16,070 It also has some programming language-like constructs. 123 00:04:16,780 --> 00:04:18,050 It has a For Each so we 124 00:04:18,170 --> 00:04:19,520 can do iteration, and it 125 00:04:19,770 --> 00:04:21,590 has conditionals so we can do if. 126 00:04:22,900 --> 00:04:24,880 All of these will be much better seen in the demo. 127 00:04:26,670 --> 00:04:27,820 Finally, I'll have to mention that 128 00:04:27,950 --> 00:04:29,500 there's some somewhat strange behavior 129 00:04:30,030 --> 00:04:31,030 having to do with white 130 00:04:31,370 --> 00:04:33,300 space in XML data and 131 00:04:33,480 --> 00:04:35,700 some default behavior, which we'll see in the demo. 132 00:04:36,580 --> 00:04:38,120 And there's also an implicit priority 133 00:04:38,750 --> 00:04:39,690 scheme when we have multiple 134 00:04:39,980 --> 00:04:41,920 templates that can all match the same elements. 135 00:04:43,290 --> 00:04:44,590 So let's move directly to the demo. 136 00:04:45,300 --> 00:04:46,230 We're again going to be using 137 00:04:46,350 --> 00:04:49,160 our same bookstore data and we'll see a number of XSLT examples. 138 00:04:50,540 --> 00:04:51,700 Even more than XQuery 139 00:04:52,100 --> 00:04:53,300 or XPath our examples 140 00:04:53,830 --> 00:04:55,200 will not be exhaustive, but they 141 00:04:55,300 --> 00:04:56,340 will give a flavor of 142 00:04:56,480 --> 00:04:57,640 the language and you'll be able 143 00:04:57,770 --> 00:04:58,950 to express some fairly powerful 144 00:04:59,440 --> 00:05:01,160 queries using just what we show in the videos. 145 00:05:02,780 --> 00:05:04,290 Now let's see XSLT in action. 146 00:05:04,780 --> 00:05:06,740 Let me first explain what we have on the screen. 147 00:05:07,590 --> 00:05:09,210 In the upper left window we 148 00:05:09,310 --> 00:05:11,130 have the document that we'll be querying over. 149 00:05:11,780 --> 00:05:12,910 It's the exact same bookstore 150 00:05:13,320 --> 00:05:15,510 data that we've been using for all of our examples. 151 00:05:16,410 --> 00:05:17,350 So I'm actually going to make 152 00:05:17,570 --> 00:05:20,560 that a lot smaller, so that we can see our templates better. 153 00:05:21,390 --> 00:05:23,610 In the upper right corner, XSLT templates. 154 00:05:24,680 --> 00:05:26,040 And every example we're going 155 00:05:26,290 --> 00:05:27,200 to do is going to have 156 00:05:27,630 --> 00:05:28,920 us opening and closing a style 157 00:05:29,210 --> 00:05:30,910 sheet with some parameters is 158 00:05:30,990 --> 00:05:32,520 to tell us how we'd like to display our results. 159 00:05:32,890 --> 00:05:33,790 And then I'll be putting 160 00:05:34,100 --> 00:05:36,260 different templates between those opening and closing tags. 161 00:05:37,120 --> 00:05:37,990 Notice again that XSLT 162 00:05:38,120 --> 00:05:39,530 is expressed using 163 00:05:39,740 --> 00:05:41,260 XML once we have 164 00:05:41,430 --> 00:05:42,670 our data and our set 165 00:05:43,130 --> 00:05:44,820 of template matching rules we'll 166 00:05:45,010 --> 00:05:47,930 run our transformation and in the bottom we'll see our result. 167 00:05:48,230 --> 00:05:49,560 So you can think of 168 00:05:49,680 --> 00:05:50,350 it as a query in the upper 169 00:05:50,480 --> 00:05:51,390 right, the data in the upper 170 00:05:51,580 --> 00:05:53,170 left, and the result displayed in the bottom. 171 00:05:54,580 --> 00:05:56,000 Now even more than XQuery, 172 00:05:56,640 --> 00:05:57,430 it's not going to be possible 173 00:05:57,960 --> 00:05:59,320 to explain every single intricacy 174 00:06:00,180 --> 00:06:01,390 of the templates that we're going to write. 175 00:06:01,550 --> 00:06:02,960 So I again encourage you 176 00:06:03,160 --> 00:06:04,320 to pause the video to take 177 00:06:04,590 --> 00:06:05,640 a look, as well as 178 00:06:05,760 --> 00:06:07,110 download the data file and 179 00:06:07,280 --> 00:06:10,090 the transformation file so that you can experiment with them yourself. 180 00:06:11,870 --> 00:06:13,080 Our first example is going 181 00:06:13,300 --> 00:06:14,880 to do some very simple template matching. 182 00:06:15,920 --> 00:06:17,310 It's going to look for 183 00:06:17,500 --> 00:06:19,230 book sub-elements and when it 184 00:06:19,400 --> 00:06:20,360 finds them, it's going to 185 00:06:20,440 --> 00:06:21,430 replace those book sub-elements 186 00:06:22,420 --> 00:06:23,950 with a book title element, 187 00:06:24,680 --> 00:06:26,080 the value of the 188 00:06:26,310 --> 00:06:28,570 title component of the book and a closing tag book title. 189 00:06:29,440 --> 00:06:30,820 And it's similarly going to 190 00:06:30,920 --> 00:06:33,020 match magazines of elements and 191 00:06:33,120 --> 00:06:34,560 replace those magazines of elements 192 00:06:35,080 --> 00:06:36,300 with an element that's an 193 00:06:36,420 --> 00:06:37,460 opening tag of magazine 194 00:06:38,020 --> 00:06:39,880 title, the value of 195 00:06:40,050 --> 00:06:42,800 the title, sub-element of the magazine, and the closing tag. 196 00:06:43,500 --> 00:06:46,290 So again the template will look through the XML tree. 197 00:06:47,040 --> 00:06:48,870 They will match the sub elements in the tree. 198 00:06:49,100 --> 00:06:51,160 It'll match the book of elements and the magazine of elements. 199 00:06:51,870 --> 00:06:52,840 And for each one it will 200 00:06:53,000 --> 00:06:54,880 replace those subelements with 201 00:06:55,150 --> 00:06:56,480 the expression, in this case 202 00:06:57,110 --> 00:06:58,280 with our opening and closing tags 203 00:06:58,690 --> 00:07:00,300 that have changed and the value of the title. 204 00:07:01,200 --> 00:07:02,820 We run the transformation and we 205 00:07:03,160 --> 00:07:04,210 see, indeed, that the results 206 00:07:04,770 --> 00:07:06,420 are our four book titles, 207 00:07:07,380 --> 00:07:08,410 now opening and closing tags 208 00:07:08,670 --> 00:07:10,710 that are book titles, and our four magazine titles. 209 00:07:11,880 --> 00:07:13,950 For our next example, we're 210 00:07:14,050 --> 00:07:16,170 going to only match books that satisfy a condition. 211 00:07:17,270 --> 00:07:20,200 We do that by in our matching expression using XPath. 212 00:07:21,170 --> 00:07:22,680 Now there's one small strange thing 213 00:07:22,890 --> 00:07:23,770 here, which is we can't write 214 00:07:23,950 --> 00:07:24,890 the less than symbol, we actually 215 00:07:25,210 --> 00:07:27,610 have to use the escape symbol for less than. 216 00:07:27,730 --> 00:07:29,940 But otherwise, this template finds 217 00:07:30,250 --> 00:07:31,930 books whose price attribute 218 00:07:32,490 --> 00:07:33,230 is less than 90, just like 219 00:07:33,310 --> 00:07:35,300 we do in XPath using the 220 00:07:35,320 --> 00:07:37,200 square brackets for conditions, and 221 00:07:37,300 --> 00:07:39,260 when it matches those books, what 222 00:07:39,440 --> 00:07:42,180 it does here is it copies those books. 223 00:07:42,420 --> 00:07:43,740 So this is an important construct 224 00:07:44,350 --> 00:07:45,670 that says if I match 225 00:07:46,040 --> 00:07:47,270 the book, I'll copy the 226 00:07:47,410 --> 00:07:48,910 book, I'll select dot which 227 00:07:49,210 --> 00:07:50,450 the current element, so in 228 00:07:50,650 --> 00:07:51,800 effect it's saying find the 229 00:07:51,880 --> 00:07:53,910 books and retain them exactly as they are. 230 00:07:54,720 --> 00:07:57,470 Let's run the transformation and take a look at what we get. 231 00:07:58,590 --> 00:07:59,490 We can see that we got 232 00:07:59,740 --> 00:08:01,320 this book because it's price 233 00:08:01,770 --> 00:08:02,710 is 85 and we have another book 234 00:08:02,960 --> 00:08:03,920 whose price is 50 and 235 00:08:04,400 --> 00:08:05,590 another book whose price is 25. 236 00:08:05,700 --> 00:08:08,540 But we do see something a little bit strange here. 237 00:08:09,630 --> 00:08:12,350 We got our books so we also have these strings here. 238 00:08:12,940 --> 00:08:14,500 These long bits of text 239 00:08:14,620 --> 00:08:16,690 that we well we don't really know where they come from. 240 00:08:17,360 --> 00:08:18,410 Well this is one of 241 00:08:18,470 --> 00:08:19,810 the peculiarities of XSLT 242 00:08:19,970 --> 00:08:21,780 . When you have 243 00:08:22,320 --> 00:08:23,640 elements in your database 244 00:08:24,200 --> 00:08:25,440 that aren't matched by any 245 00:08:25,620 --> 00:08:27,470 template, what XSLT will 246 00:08:27,670 --> 00:08:28,870 do is actually return the 247 00:08:28,940 --> 00:08:30,300 concatenation of the string 248 00:08:31,080 --> 00:08:33,810 leaf or text leaf values of those elements. 249 00:08:34,740 --> 00:08:35,690 I know it seems kind of strange. 250 00:08:36,050 --> 00:08:37,550 There's actually a simple fix for that. 251 00:08:38,270 --> 00:08:39,220 We're going to add a second 252 00:08:39,830 --> 00:08:41,710 template that matches those 253 00:08:42,420 --> 00:08:45,150 text elements and for those returns nothing. 254 00:08:46,000 --> 00:08:47,930 So here we've added a template and let me explain. 255 00:08:48,810 --> 00:08:50,990 What we're matching here is elements 256 00:08:51,440 --> 00:08:53,020 that satisfy the text predicates 257 00:08:53,570 --> 00:08:54,510 so that will match those leaf 258 00:08:54,880 --> 00:08:56,300 text elements and when 259 00:08:56,530 --> 00:08:58,260 we write a template that has 260 00:08:58,560 --> 00:08:59,930 no body, so we open 261 00:09:00,240 --> 00:09:01,120 the template and then we 262 00:09:01,230 --> 00:09:02,370 close the template with no 263 00:09:02,570 --> 00:09:03,770 body at all, that says 264 00:09:04,030 --> 00:09:05,320 match the element and then 265 00:09:05,510 --> 00:09:07,040 don't replace with anything at all. 266 00:09:07,560 --> 00:09:08,600 So this is very useful construct, 267 00:09:09,180 --> 00:09:10,150 the templates that don't have 268 00:09:10,350 --> 00:09:11,550 a body, for getting rid 269 00:09:11,720 --> 00:09:13,400 of portions of the data we're not interested in. 270 00:09:14,260 --> 00:09:15,210 So let's run the transformation 271 00:09:16,080 --> 00:09:17,150 now and take a look 272 00:09:17,270 --> 00:09:18,520 at the result, and now when 273 00:09:18,660 --> 00:09:19,880 we scroll down we see that 274 00:09:20,090 --> 00:09:22,140 all of that extraneous text that 275 00:09:22,950 --> 00:09:25,100 we saw in the previous example is now gone. 276 00:09:26,430 --> 00:09:28,340 So as we've seen, XSLT works 277 00:09:28,840 --> 00:09:31,450 by defining templates that are matched against the data. 278 00:09:32,490 --> 00:09:33,350 When a portion of the data 279 00:09:33,570 --> 00:09:35,630 is matched by a template the template says what to do. 280 00:09:36,040 --> 00:09:37,060 We might place that portion 281 00:09:37,420 --> 00:09:38,640 of data with something different and 282 00:09:38,730 --> 00:09:39,920 we might just remove that portion 283 00:09:40,250 --> 00:09:42,820 of the data from the answer or we might just copy it over into the answer. 284 00:09:42,900 --> 00:09:44,970 Now let's explore what 285 00:09:45,150 --> 00:09:46,170 happens when we have 286 00:09:46,550 --> 00:09:47,830 portions of the data that are 287 00:09:48,060 --> 00:09:49,230 matched by more than one template 288 00:09:49,860 --> 00:09:50,830 in our XSLT specification. 289 00:09:52,340 --> 00:09:53,830 So here we're going to have three templates. 290 00:09:55,040 --> 00:09:57,870 The first two templates both match book elements. 291 00:09:58,650 --> 00:10:00,070 The first template says when 292 00:10:00,190 --> 00:10:01,750 we match a book element, just throw it away. 293 00:10:02,260 --> 00:10:03,500 Again, this is an example of 294 00:10:03,570 --> 00:10:04,570 the template when we don't have 295 00:10:04,680 --> 00:10:06,230 a body that says eliminate 296 00:10:06,830 --> 00:10:08,000 the matched elements from the answer. 297 00:10:09,060 --> 00:10:11,230 The second template says to do exactly the opposite. 298 00:10:12,060 --> 00:10:13,140 Says when we match a book 299 00:10:13,420 --> 00:10:14,610 sub-element, keep that book 300 00:10:15,430 --> 00:10:16,990 sub-element exactly as it is. 301 00:10:17,450 --> 00:10:18,890 As a reminder, this body here 302 00:10:19,450 --> 00:10:22,060 says copy the current element into the result. 303 00:10:23,400 --> 00:10:24,780 Our third template matches magazines, 304 00:10:25,650 --> 00:10:26,600 and this one we just have one 305 00:10:26,890 --> 00:10:28,600 and it says copy the magazine into the result. 306 00:10:29,610 --> 00:10:30,970 So let's go ahead and run 307 00:10:31,330 --> 00:10:33,060 this transformation and see what happens. 308 00:10:34,090 --> 00:10:35,150 Well, first of all, we got 309 00:10:35,580 --> 00:10:37,470 an ambiguous rule match, so that's good. 310 00:10:37,780 --> 00:10:39,580 The system recognized that we 311 00:10:39,750 --> 00:10:42,100 have two different rules that are matching the same element. 312 00:10:42,940 --> 00:10:44,480 But then it did decide to give us a result. 313 00:10:44,930 --> 00:10:46,080 So let's take a look at what happened. 314 00:10:46,550 --> 00:10:47,650 It did return, in fact, 315 00:10:48,070 --> 00:10:49,130 all of the books in 316 00:10:49,220 --> 00:10:50,840 the database as well as all the magazines. 317 00:10:51,960 --> 00:10:52,870 So we can see that it 318 00:10:53,030 --> 00:10:54,010 chose to use the second 319 00:10:54,500 --> 00:10:55,920 template instead of the 320 00:10:56,070 --> 00:10:57,790 first template when we had the ambiguity. 321 00:10:59,270 --> 00:11:00,220 So let's try an experiment. 322 00:11:00,930 --> 00:11:02,230 Let's take our two book 323 00:11:03,570 --> 00:11:05,570 templates and let's just reverse their order. 324 00:11:06,380 --> 00:11:07,200 So now we have the one 325 00:11:07,500 --> 00:11:09,870 that copies first, and the one that eliminates second. 326 00:11:10,710 --> 00:11:13,780 Let's run the transformation, and indeed, something changed. 327 00:11:14,340 --> 00:11:15,510 We no longer got the books. 328 00:11:16,550 --> 00:11:17,550 So what we can deduce from 329 00:11:17,760 --> 00:11:18,900 that is that when we 330 00:11:18,900 --> 00:11:20,210 have two templates that both 331 00:11:20,480 --> 00:11:21,960 match and we get this 332 00:11:22,290 --> 00:11:24,450 ambiguity warning, it still 333 00:11:24,900 --> 00:11:26,170 does the transformation and it chooses 334 00:11:26,610 --> 00:11:27,930 the second of the matching transformations. 335 00:11:29,920 --> 00:11:32,530 Actually, it turns out not to be quite that simple. 336 00:11:32,960 --> 00:11:34,510 It doesn't always choose the second one. 337 00:11:35,570 --> 00:11:36,520 In this example, we're going 338 00:11:36,690 --> 00:11:38,140 to change our first template 339 00:11:38,250 --> 00:11:41,120 to match only books whose price is less than 90. 340 00:11:41,410 --> 00:11:43,850 So we'll use the same syntax we used before that before. 341 00:11:44,250 --> 00:11:46,900 We have to escape that less than character like this. 342 00:11:47,730 --> 00:11:48,490 Less than 90. 343 00:11:49,040 --> 00:11:50,010 Close our score bracket. 344 00:11:50,880 --> 00:11:52,240 So now our first transformation says 345 00:11:52,410 --> 00:11:53,450 when we find books that are 346 00:11:53,560 --> 00:11:54,860 less than 90, let's 347 00:11:55,220 --> 00:11:56,760 return them, and when 348 00:11:56,950 --> 00:11:59,500 we find any book, let's not return it. 349 00:11:59,700 --> 00:12:01,810 So again we're going to have some ambiguity; let's run the transformation. 350 00:12:03,070 --> 00:12:04,310 Well, we actually didn't get 351 00:12:04,480 --> 00:12:05,740 an ambiguity error this time, 352 00:12:06,010 --> 00:12:07,510 or warning, and the reason 353 00:12:07,670 --> 00:12:09,570 is that XSLT actually has 354 00:12:09,680 --> 00:12:11,100 a built-in notion of some templates 355 00:12:11,550 --> 00:12:13,900 being more specific than others, and 356 00:12:14,040 --> 00:12:15,460 when a template is more specific, 357 00:12:15,590 --> 00:12:17,680 it is considered the higher priority template. 358 00:12:18,670 --> 00:12:19,660 So what happened when we ran 359 00:12:19,920 --> 00:12:21,970 this particular transformation is the 360 00:12:22,250 --> 00:12:23,080 books that, where the price 361 00:12:23,540 --> 00:12:25,120 was less than 90, were matched 362 00:12:25,560 --> 00:12:26,690 by the first template and because 363 00:12:27,160 --> 00:12:28,670 that one's considered more specific they 364 00:12:28,780 --> 00:12:30,400 were not matched by the second template. 365 00:12:31,370 --> 00:12:32,840 So we can see below that 366 00:12:32,980 --> 00:12:34,500 we did get back all 367 00:12:34,710 --> 00:12:35,700 of the books that are less than 368 00:12:35,880 --> 00:12:37,190 90, and none of the 369 00:12:37,300 --> 00:12:38,800 other books, and again, we 370 00:12:39,350 --> 00:12:40,450 got back all of our magazines. 371 00:12:41,990 --> 00:12:43,640 So let's make one last change to experiment. 372 00:12:44,340 --> 00:12:45,730 Let's take our second book, 373 00:12:46,600 --> 00:12:47,810 and let's add to it a 374 00:12:47,930 --> 00:12:49,570 simple condition that's satisfied by 375 00:12:50,010 --> 00:12:50,910 every book, which is the condition 376 00:12:51,460 --> 00:12:53,040 that the book has a title sub-element. 377 00:12:53,630 --> 00:12:54,160 Again, this is XPath. 378 00:12:55,670 --> 00:12:57,330 Now, perhaps our two 379 00:12:57,670 --> 00:12:59,650 rules have equivalent specificity, 380 00:13:00,540 --> 00:13:02,020 in which we case we would again have ambiguity. 381 00:13:03,100 --> 00:13:04,340 Let's just delete our result here 382 00:13:04,950 --> 00:13:06,830 and then let's run the transformation and see what happens. 383 00:13:07,600 --> 00:13:08,730 Indeed, now we have an 384 00:13:08,810 --> 00:13:10,580 ambiguous rule match because both 385 00:13:11,030 --> 00:13:12,780 of these templates have 386 00:13:12,940 --> 00:13:14,300 a condition, so they are 387 00:13:14,600 --> 00:13:16,230 considered equivalent again, just when, 388 00:13:17,060 --> 00:13:18,270 just like when neither of them had a condition. 389 00:13:18,840 --> 00:13:20,100 And now that they're 390 00:13:20,190 --> 00:13:22,140 considered equivalent, again the 391 00:13:22,340 --> 00:13:23,470 second one is going to 392 00:13:23,550 --> 00:13:25,180 take precedence, because as you 393 00:13:25,280 --> 00:13:26,890 can see we didn't get any books in our result. 394 00:13:27,510 --> 00:13:28,190 So even though we have some 395 00:13:28,550 --> 00:13:29,440 books that are less than 90, 396 00:13:30,000 --> 00:13:31,300 those books also have a 397 00:13:31,350 --> 00:13:32,520 title, so those books 398 00:13:32,770 --> 00:13:34,880 were matched by the second template and they were not returned. 399 00:13:36,280 --> 00:13:37,210 So what you can see from these 400 00:13:37,380 --> 00:13:38,360 examples is that you 401 00:13:38,490 --> 00:13:39,520 do need to be very careful 402 00:13:40,060 --> 00:13:42,030 when you write XSLT programs or 403 00:13:42,110 --> 00:13:44,960 queries, where multiple templates will match the same data. 404 00:13:46,830 --> 00:13:47,490 Now let's look at a couple 405 00:13:47,840 --> 00:13:49,250 of different ways of copying our 406 00:13:49,390 --> 00:13:51,440 entire input data as a result of our query. 407 00:13:52,920 --> 00:13:54,410 Our first example is the simplest one. 408 00:13:54,920 --> 00:13:57,620 We write a template that matches the root element of the document. 409 00:13:58,180 --> 00:14:00,870 As you may remember from XPath, a single slash is the root element. 410 00:14:01,820 --> 00:14:03,160 And then as the body we have 411 00:14:03,480 --> 00:14:04,940 that copy of template that 412 00:14:05,360 --> 00:14:06,720 copies the entire current element. 413 00:14:07,620 --> 00:14:09,420 Let's run the transformation and we 414 00:14:09,560 --> 00:14:10,420 will see the we get our 415 00:14:10,570 --> 00:14:12,100 entire database as a result. 416 00:14:13,070 --> 00:14:15,080 Incidentally we could change that slash to be bookstore. 417 00:14:15,620 --> 00:14:16,690 It would do exactly the same 418 00:14:16,950 --> 00:14:18,970 thing, since our bookstore is our root element. 419 00:14:19,820 --> 00:14:22,370 Okay, delete this, run the 420 00:14:22,440 --> 00:14:23,800 transform, and once again, 421 00:14:23,990 --> 00:14:25,580 we get the entire database as our result. 422 00:14:27,050 --> 00:14:28,190 Now I'm going to show 423 00:14:28,770 --> 00:14:29,950 action with a much more complicated 424 00:14:30,630 --> 00:14:31,850 way of copying the entire document, 425 00:14:31,960 --> 00:14:33,280 but it uses an important 426 00:14:33,940 --> 00:14:36,100 kind of template that we'll see in other contexts. 427 00:14:37,390 --> 00:14:38,640 This template is our first 428 00:14:38,910 --> 00:14:40,810 example of recursively applying 429 00:14:41,380 --> 00:14:42,450 templates to our result. 430 00:14:43,580 --> 00:14:44,770 What we have here is a 431 00:14:44,840 --> 00:14:47,820 template that matches absolutely anything in XML data. 432 00:14:48,460 --> 00:14:50,400 This is actually an ex-path-expression that says 433 00:14:50,620 --> 00:14:51,980 math an element with star, 434 00:14:52,290 --> 00:14:54,340 that means any element tag, any 435 00:14:54,690 --> 00:14:56,790 attribute, at star, or 436 00:14:57,180 --> 00:14:59,790 any text leaf of the XML data. 437 00:15:00,300 --> 00:15:02,240 So again this or-construct here 438 00:15:02,730 --> 00:15:03,990 is seen quite frequently in XSLT 439 00:15:04,570 --> 00:15:05,870 specifications to match just 440 00:15:06,740 --> 00:15:07,570 anything at all in the data. 441 00:15:08,730 --> 00:15:09,980 When anything at all is matched 442 00:15:10,650 --> 00:15:11,760 that element of the 443 00:15:11,890 --> 00:15:13,340 data is copied, and then 444 00:15:13,800 --> 00:15:15,340 the templates are applied recursively 445 00:15:16,630 --> 00:15:19,070 to everything below that's of any type. 446 00:15:20,210 --> 00:15:21,380 So it may be best just to 447 00:15:21,510 --> 00:15:22,450 take my word for it, or 448 00:15:22,810 --> 00:15:23,720 you can spend some time on 449 00:15:23,830 --> 00:15:25,050 your own thinking about exactly why 450 00:15:25,250 --> 00:15:26,400 this works but, again, the 451 00:15:26,480 --> 00:15:27,700 idea that we match any 452 00:15:28,230 --> 00:15:29,340 type of element in our 453 00:15:29,450 --> 00:15:31,700 XML element, attribute or text, 454 00:15:32,010 --> 00:15:34,910 and we copy that object, and 455 00:15:35,040 --> 00:15:36,000 then we apply the templates 456 00:15:36,480 --> 00:15:37,760 to all of its sub-elements recursively, 457 00:15:38,700 --> 00:15:39,410 again copying them. 458 00:15:39,930 --> 00:15:40,940 Now obviously this is not 459 00:15:41,210 --> 00:15:43,400 the best, the easiest way to copy an entire document. 460 00:15:43,860 --> 00:15:44,880 We saw the easiest way to 461 00:15:44,950 --> 00:15:46,250 do it with our previous example, but 462 00:15:46,380 --> 00:15:48,580 we'll soon see why this particular template is valuable. 463 00:15:49,620 --> 00:15:51,900 When we run it, of course we get back the entire document. 464 00:15:53,490 --> 00:15:54,470 Now the reason that this type 465 00:15:54,670 --> 00:15:56,440 of template is valuable is that 466 00:15:56,570 --> 00:15:57,470 we can use this as one 467 00:15:57,830 --> 00:15:59,490 of our templates and then add 468 00:16:00,170 --> 00:16:01,730 additional templates that give 469 00:16:01,900 --> 00:16:03,530 us exceptions to copying the whole document. 470 00:16:04,290 --> 00:16:05,380 And that will allow us to 471 00:16:05,670 --> 00:16:06,960 copy the whole document except 472 00:16:07,380 --> 00:16:08,610 with changes in certain parts, 473 00:16:09,650 --> 00:16:12,100 and what I'm adding here actually is a whole bunch of additional templates. 474 00:16:13,060 --> 00:16:14,430 So the first one says: apply 475 00:16:14,990 --> 00:16:17,040 all templates recursively to link to the entire document. 476 00:16:18,100 --> 00:16:19,580 The second says, "When you 477 00:16:19,830 --> 00:16:20,950 find while you're applying them 478 00:16:21,060 --> 00:16:22,240 recursively that you're at 479 00:16:22,340 --> 00:16:25,530 an attribute called ISBN, we'll change that to a sub-element." 480 00:16:26,150 --> 00:16:27,780 So we'll match the ISBN attribute. 481 00:16:28,580 --> 00:16:29,580 We'll change it to a 482 00:16:29,740 --> 00:16:30,870 sub-element, similarly to what we saw 483 00:16:31,040 --> 00:16:31,870 before by giving an open 484 00:16:32,220 --> 00:16:35,010 tag ISBN and the value of the current element. 485 00:16:35,760 --> 00:16:37,430 We'll similarly take our attributes, 486 00:16:37,870 --> 00:16:38,920 our price attributes, and change 487 00:16:39,580 --> 00:16:40,320 them to sub-elements and our 488 00:16:40,390 --> 00:16:42,250 editions, our months, and our years and our magazine. 489 00:16:43,460 --> 00:16:45,550 And last of all, we'll also make a change to our authors. 490 00:16:46,060 --> 00:16:47,530 When we match an author, instead 491 00:16:47,880 --> 00:16:49,140 of having sub-elements, we'll convert 492 00:16:49,650 --> 00:16:51,060 those sub-elements to be attributes, 493 00:16:51,690 --> 00:16:53,730 the last name attribute and the first name attribute. 494 00:16:54,890 --> 00:16:56,530 So let's run the transformation, and 495 00:16:56,690 --> 00:16:58,490 we'll see our data is now significantly restructured. 496 00:16:59,750 --> 00:17:00,790 We have our bookstore and we 497 00:17:00,850 --> 00:17:01,850 have our books, but our ISBN 498 00:17:02,340 --> 00:17:03,280 numbers are now sub-elements, and 499 00:17:04,070 --> 00:17:05,320 in our authors the last names 500 00:17:05,690 --> 00:17:06,950 and first names are attributes. 501 00:17:07,840 --> 00:17:08,550 And all of the books 502 00:17:08,800 --> 00:17:09,910 are restructured in that fashion 503 00:17:10,300 --> 00:17:12,220 and our magazines again have attributes 504 00:17:13,030 --> 00:17:14,070 restructured as sub-elements. 505 00:17:15,610 --> 00:17:16,590 Now let's see what would 506 00:17:16,750 --> 00:17:18,000 have happened if we ran this 507 00:17:18,440 --> 00:17:20,130 XSLT specification but we 508 00:17:20,320 --> 00:17:21,830 didn't have this mega 509 00:17:22,300 --> 00:17:23,880 template at the beginning that 510 00:17:24,030 --> 00:17:26,630 does the recursive application of templates to the entire database. 511 00:17:27,600 --> 00:17:28,970 When we run the transformation now, 512 00:17:29,350 --> 00:17:31,370 well, we get a kind of surprising result. 513 00:17:32,130 --> 00:17:34,020 We won't try to analyze it in its entirety. 514 00:17:34,890 --> 00:17:36,860 It's a combination of only 515 00:17:37,290 --> 00:17:40,390 matching automatically of sub-elements and not attributes. 516 00:17:41,010 --> 00:17:42,360 And furthermore, dumping out all 517 00:17:42,500 --> 00:17:44,560 the text leaves like we saw in an earlier example. 518 00:17:45,910 --> 00:17:47,020 So, again, presuming that we would 519 00:17:47,240 --> 00:17:48,170 not want this to be our 520 00:17:48,270 --> 00:17:49,420 result, that shows the 521 00:17:49,820 --> 00:17:51,470 necessity of including the sort 522 00:17:51,650 --> 00:17:53,190 of generic template that matches 523 00:17:54,040 --> 00:17:55,190 every type of object in 524 00:17:55,280 --> 00:17:58,100 the database, and recursively applies templates to its children. 525 00:17:59,330 --> 00:18:00,500 Now let's switch gears entirely. 526 00:18:01,280 --> 00:18:02,610 What we're going to do 527 00:18:02,710 --> 00:18:04,940 in this transformation is effectively write a program. 528 00:18:05,740 --> 00:18:06,690 We're going to use the "for 529 00:18:07,030 --> 00:18:08,560 each" and "sorting" and 530 00:18:08,920 --> 00:18:10,560 an "If" statement, and the 531 00:18:10,790 --> 00:18:12,180 program is furthermore going to 532 00:18:12,300 --> 00:18:13,570 take the XML data and it's 533 00:18:13,670 --> 00:18:15,010 going to transform it into HTML, 534 00:18:15,730 --> 00:18:17,060 which we can then render in a browser. 535 00:18:17,900 --> 00:18:19,230 So it's just one template 536 00:18:19,820 --> 00:18:21,000 that matches the root element of 537 00:18:21,080 --> 00:18:22,620 our document, and once that 538 00:18:22,800 --> 00:18:24,090 root element is matched, it spits 539 00:18:24,360 --> 00:18:25,930 out the tag HTML, it sets 540 00:18:25,990 --> 00:18:27,010 up the table, so again we're 541 00:18:27,160 --> 00:18:28,550 actually writing the result here, 542 00:18:29,360 --> 00:18:30,470 and put some headers for the table. 543 00:18:31,010 --> 00:18:32,130 And then we see a 544 00:18:32,160 --> 00:18:33,040 for each that says we're going to 545 00:18:33,920 --> 00:18:36,220 run the body of the for each for each book in the database. 546 00:18:37,160 --> 00:18:38,870 We're gonna sort the result by its price. 547 00:18:40,130 --> 00:18:41,020 If the price is less than 548 00:18:41,100 --> 00:18:43,150 90, then we're going to generate a row in the table. 549 00:18:43,690 --> 00:18:44,580 And that row is going 550 00:18:44,820 --> 00:18:46,100 to be set up with italics for 551 00:18:46,250 --> 00:18:47,480 the title, and it's going 552 00:18:47,690 --> 00:18:48,600 to give the value of the price, 553 00:18:49,450 --> 00:18:50,100 it's going to close the row, 554 00:18:50,520 --> 00:18:51,780 and we're going to close all the tags. 555 00:18:52,170 --> 00:18:54,680 So again, this is quite different in a couple of ways. 556 00:18:55,040 --> 00:18:55,740 First of all, that it's written 557 00:18:55,990 --> 00:18:57,690 more in a programmatic style, and 558 00:18:57,790 --> 00:18:59,980 second of all that the result actually going to be HTML. 559 00:19:01,140 --> 00:19:02,990 Let's run the transformation, and we 560 00:19:03,140 --> 00:19:05,510 can see the result here, which is indeed HTML. 561 00:19:05,790 --> 00:19:07,320 In fact, we can 562 00:19:07,460 --> 00:19:09,160 take this very HTML and we 563 00:19:09,400 --> 00:19:11,530 can render it in a browser and see how nice it looks. 564 00:19:12,470 --> 00:19:13,690 And here it is. 565 00:19:14,920 --> 00:19:17,120 We can see very beautifully formatted 566 00:19:17,630 --> 00:19:18,860 the three books that cost less 567 00:19:19,070 --> 00:19:20,280 than 90, sorted by price, 568 00:19:20,820 --> 00:19:22,360 with the title in italics, all 569 00:19:22,620 --> 00:19:23,930 formatted in an HTML table. 570 00:19:24,330 --> 00:19:25,470 And that was with not 571 00:19:25,680 --> 00:19:27,360 a very complicated XSLT program. 572 00:19:28,280 --> 00:19:29,460 So it's not surprising that XSLT 573 00:19:30,570 --> 00:19:31,950 is used frequently for for 574 00:19:32,370 --> 00:19:33,850 translating data expressed in 575 00:19:33,950 --> 00:19:35,590 XML to HTML format 576 00:19:35,990 --> 00:19:38,280 for rendering, as well as being used as a query language. 577 00:19:39,770 --> 00:19:41,390 Our last two examples are back 578 00:19:41,650 --> 00:19:43,370 to a more traditional template matching style. 579 00:19:44,420 --> 00:19:45,430 Again we're going to start 580 00:19:45,580 --> 00:19:48,300 with this recursive template match that matches everything in the database. 581 00:19:48,890 --> 00:19:49,970 That means we're gonna copy everything 582 00:19:50,440 --> 00:19:52,760 over except we're gonna make one type of change. 583 00:19:53,360 --> 00:19:54,720 Specifically, we're going to 584 00:19:54,780 --> 00:19:56,000 change...we're going to take 585 00:19:56,410 --> 00:19:57,210 Jennifer out of the database 586 00:19:58,250 --> 00:20:00,490 and then we're going to change Widom to Ms. Widom. 587 00:20:01,290 --> 00:20:02,310 So every place where we have 588 00:20:02,620 --> 00:20:03,440 Jennifer as the first name 589 00:20:03,610 --> 00:20:04,940 and Widom as the last name, we'll 590 00:20:05,110 --> 00:20:06,610 end up with just a name, Ms. Widom. 591 00:20:07,280 --> 00:20:08,520 Specifically, we do it with two templates. 592 00:20:09,520 --> 00:20:11,010 The first template says when we 593 00:20:11,300 --> 00:20:13,030 find a first name where 594 00:20:13,200 --> 00:20:14,510 the data in that first 595 00:20:14,750 --> 00:20:16,690 name equals Jennifer...okay, so we're 596 00:20:16,960 --> 00:20:18,880 again are using the dot to refer to the current element. 597 00:20:19,280 --> 00:20:20,190 The data is a built-in function. 598 00:20:21,080 --> 00:20:22,410 So a first name that's equal to Jennifer. 599 00:20:23,660 --> 00:20:25,030 When we match that, we 600 00:20:25,300 --> 00:20:27,070 want to...we'll actually return nothing. 601 00:20:27,520 --> 00:20:30,170 There's no body in this template, so that will remove that element. 602 00:20:30,970 --> 00:20:31,960 Now you might wonder why we 603 00:20:32,100 --> 00:20:34,510 didn't just write a condition that said first name equals Jennifer. 604 00:20:35,320 --> 00:20:36,220 The problem is, to write that 605 00:20:36,330 --> 00:20:37,720 condition, the current element 606 00:20:38,070 --> 00:20:39,240 would be the parent, and we 607 00:20:39,380 --> 00:20:40,260 don't want to remove the parent, 608 00:20:40,770 --> 00:20:42,530 we actually want to remove the first name itself. 609 00:20:42,830 --> 00:20:44,440 In addition to removing 610 00:20:45,020 --> 00:20:46,650 first names that are Jennifer, we'll 611 00:20:46,750 --> 00:20:48,090 also match last name 612 00:20:48,310 --> 00:20:49,270 templates where the value 613 00:20:49,620 --> 00:20:50,920 is Widom and we will replace 614 00:20:51,640 --> 00:20:52,910 those with an opening 615 00:20:53,230 --> 00:20:56,020 tag name, the string is Widom, and a closing tag name. 616 00:20:56,710 --> 00:20:59,290 So let's run the transformation and let's take a look. 617 00:21:00,230 --> 00:21:01,360 And we will see in the 618 00:21:01,430 --> 00:21:02,850 case where the author was Jennifer 619 00:21:03,250 --> 00:21:04,250 Widom, it's now the single 620 00:21:04,840 --> 00:21:06,260 element name Ms. Widom, and 621 00:21:06,910 --> 00:21:08,140 we should see that occur a 622 00:21:08,400 --> 00:21:10,140 few other times in the database as well. 623 00:21:11,700 --> 00:21:13,200 As our very last example, let's 624 00:21:13,490 --> 00:21:15,670 perform the same transformation, but 625 00:21:15,810 --> 00:21:17,350 let's do it with just one template. 626 00:21:18,480 --> 00:21:19,370 What we'll do is we'll look 627 00:21:19,510 --> 00:21:21,120 for office of elements 628 00:21:21,970 --> 00:21:23,740 where the first name equals Widom. 629 00:21:24,410 --> 00:21:25,330 Now we don't need to use data. 630 00:21:26,590 --> 00:21:28,000 So first name equals Widom. 631 00:21:29,610 --> 00:21:30,900 And we'll take those entire 632 00:21:31,570 --> 00:21:33,310 author sub-elements and we'll replace 633 00:21:33,830 --> 00:21:35,150 them with an author 634 00:21:35,560 --> 00:21:37,850 sub-element where the name is Widom. 635 00:21:38,550 --> 00:21:39,700 So we need to put author here. 636 00:21:40,700 --> 00:21:42,360 Let's get rid of this automatic 637 00:21:42,840 --> 00:21:45,210 simply generated closing tab; we want it to be over here. 638 00:21:49,340 --> 00:21:50,680 We'll get rid of this first template. 639 00:21:51,240 --> 00:21:51,910 So again, we're going to make exactly 640 00:21:52,590 --> 00:21:54,330 the same change, but we're gonna do it with a single template. 641 00:21:54,880 --> 00:21:55,720 It's going to look for authors 642 00:21:56,230 --> 00:21:59,220 where the first name is...whoops, better make that Jennifer. 643 00:22:00,310 --> 00:22:01,320 And it's going to replace 644 00:22:01,850 --> 00:22:03,710 them with the author 645 00:22:04,010 --> 00:22:05,260 sub-element with just Ms. Widom. 646 00:22:05,760 --> 00:22:07,770 We run the transformation and let's 647 00:22:08,120 --> 00:22:09,340 take a quick look at what we got. 648 00:22:09,650 --> 00:22:11,340 And we again see exactly 649 00:22:11,640 --> 00:22:13,880 the same result, with a somewhat simpler program. 650 00:22:15,970 --> 00:22:17,410 That concludes our demonstration of XSLT. 651 00:22:18,810 --> 00:22:20,470 Again, we've shown only some of the constructs. 652 00:22:21,370 --> 00:22:23,620 We haven't gone into great detail or walked through the syntax. 653 00:22:24,640 --> 00:22:25,970 XSLT is very powerful. 654 00:22:26,490 --> 00:22:27,720 We've seen quite a few different things. 655 00:22:27,950 --> 00:22:30,410 We've also seen a little bit of non-intuitive behavior. 656 00:22:30,850 --> 00:22:32,950 We have to be a little careful with white space. 657 00:22:33,340 --> 00:22:34,210 We have to be a little careful 658 00:22:34,620 --> 00:22:36,870 when we have multiple templates that match the same data. 659 00:22:37,180 --> 00:22:38,520 But once we get 660 00:22:38,790 --> 00:22:39,970 it all figured out, it can 661 00:22:40,110 --> 00:22:43,020 be quite powerful for transforming data and for querying data.