I thought that this debate was over some time ago even if by each camp staying entrenched in their own beliefs. Ben Bryant does not think so and writes the following in his Transformation Example: Apples to Oranges:
It is truly impressive that some developers have mastered a challenging technology such as XSLT which makes any moderately complex task nearly impossible. But is it really worth it? It is funny to me when someone being helpful on Microsoft XSL newsgroup produces a big long stylesheet solution that appears absolutely cryptic.
So according to Ben the XSLT is ill suited for a task like the one described in his post. I suggest you first go and check it out - it's not a long read and boils down to this - if you want to solve a certain class of problems, CMarkup (Ben's product) is the way to go.
Naturally I had to try to prove him wrong ;) I can't blame Ben for advertising his own product, but this is a poor choice of a problem - the problem did not look as something XSLT interpreter/compiler would waste 20 seconds on (using his test case - 6 elements each with 6 child elements, resulting file size about 1.7MB). I first tried the XSLT he described as cryptic (and to a certain degree I agree that it is not very readable). It did not take longer than two seconds on a reasonably fast (or slow) Pentium-M (600MHz when idle that shortly spikes to 1.7GHz while transforming).
Then I went on to produce my own XSLT at fist thinking I might need XSLT 2.0, but it turned out there's nothing here that can't be solved with XSLT 1.0. You could argue that I cheated because I used EXSLT, but that's not really fair - for some reason basic set of math functions in XSLT does not include power; at the same time, EXSLT is an industry standard way of extending XSLT that is present on practically every platform and implementation. Besides, I only used EXSLT for the power function and nothing else. My console test runner was nxslt that itself uses .NET Framework 2.0 compiled XSLT transformer. But first, the stylesheet:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math">
<xsl:output method="text" omit-xml-declaration="yes" encoding="iso-8859-1"/>
<xsl:template match="columns">
<xsl:apply-templates select="column[1]"/>
</xsl:template>
<xsl:template match="column">
<xsl:param name="running" select="''"/>
<xsl:param name="index" select="1"/>
<xsl:variable name="recurse" select="following-sibling::*"/>
<xsl:variable name="increment" select="math:power(6, count($recurse))"/>
<xsl:for-each select="col">
<xsl:variable name="current_running" select="concat($running, ' ', .)"/>
<xsl:variable name="current_index" select="$index + (position() - 1) * $increment"/>
<xsl:if test="$recurse">
<xsl:apply-templates select="$recurse[1]">
<xsl:with-param name="running" select="$current_running"/>
<xsl:with-param name="index" select="$current_index"/>
</xsl:apply-templates>
</xsl:if>
<xsl:if test="not($recurse)">
<xsl:value-of select="concat($current_index, $current_running, ' ')"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
[Edit: as Ben points out in his response, I hard-coded the number of elements to 6; this is easily fixed by changing 6 to count(*) but the fact remains - I assumed same number of child elements of column element] Of course, if you don't know XSLT this will still look cryptic to you. But if you do understand basic XSLT it should make all the sense, is very short and runs sub-second including the compilation of the stylesheet. It generates 66 rows (list of all combinations [no repeats] of nodes) - about 1.7MB of text, exactly in line with his own test.
Should I conclude that because of this XSLT is better (or at least equal to) than C++ based solution (CMarkup)? Definitely not. If the transformation involved more of the processing that was highly iterative and best suited for a language that allows changes to variables (like C#, C++ or Java) I am sure that using purely XSLT would yield a really ugly solution whereas CMarkup (C++) based one would still look nice and clean. In fact if I wanted I'd probably come up with a better use case for CMarkup where C++ would really shine ;)
Moral of the story is (again) use the right tool for the right job. If all you have is a hammer, don't look at every problem as if it is a nail :)
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
In yet another great article (the content is as usual well-known thing, but the story telling is top notch) Joel is bitchin' about today's kids and stuff they're taught (or more precisely things they are not taught) at the university.
Apparently I had all the right ingredients (according to Joel) for a decent CS grad:
- Pascal, interestingly we used it for an “Interpreters and compilers“ course
- C (pointers, recursion and all that Jazz was an essential ingredient)
- Lisp (this one should make Paul Graham happy)
- Prolog - this one was tough for me
- artificial assembly language (to grasp the concepts) for which I actually wrote an interpreter and debugger; some of my colleagues successfully used it to prove that their code was running alright even before the results of the written test were published ;)
- real assembly language for Intel 80386
- relational algebra and calculus - this was a pre-requisite for SQL and database development; then we went on to use embedded SQL from C :)
- absolute geometry - one of the toughest things you could face; it starts in total vacuum, with only about 5 axioms and builds everything from scratch - we were unable to reuse any previous mathematical knowledge; the whole thing is completely abstract and only deals with Euclidean geometry of space as you know it as a special case at the very end of the book :)
- shitload of other math, not all of which is directly applicable to CS, except for numerical mathematics and analytical geometry (will come real handy for 3D in Avalon)
I also attended a course on Haskell some time after I graduated but as a language Haskell and tools wasn't quite mature at the time. You will notice that I did not have a C++ course - I had to learn that one myself (at the time I naively believed that it's only a small incremental improvement of C).
Before university I used (ZX Spectrum) Basic, assembly language for Z80 and 6510 (8-bit processors that ruled the eighties), Cobol (in high school, never tried to run anything on a real machine) and Pascal on Amiga (16-bit, using Motorola 68000).
I think (and Joel seems to think along the same lines) that all the stuff I accrued over the years gives me distinct advantage over those that can only do (quote from Joel's article) Yet Another Java Accounting Application. But it's still not enough - that's why even though I technically don't have to (my job does not require it) I am still learning, especially things that are distinctly different from my everyday job - things like Ruby, Haskell and XSLT 2. Have a look at the right sidebar of this blog and you'll see the books I own (I really should change the title, this way it might sound like I wrote all those books).
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
In a thought provoking post I See Markup, Part 5 - Not Chasing Standards Ben complains about the extra work that is needed for developers to support various XML standards.
On the other hand, he should probably be thankful because it's precisely because of this that he has a sustainable business :) Ben is the author of CMarkup, a lightweight C++ parser for XML that satisfies a niche. Those that want a fast solution that is trivial to integrate into their C++ code base probably should be happy with his product. But things are not as simple as they look - for example, Ben claims that:
But I never saw the business case for XSLT 1.0 let alone 2.0. To me, solutions formed around XML transformation by style sheet and even validation by DTD/Schema are "artificial". I call them artificial, because they create work rather than solving a problem. They are unnecessary detours because programmers would otherwise transform XML for viewing in more direct ways and validate their data in more natural and reliable ways, using tools they are already familiar with.
The truth is as usual in the eye of the beholder. For some of Ben's users, his product is God-given; they don't have to learn anything new. But this is precisely the reason why it might be dangerous to look into the world of XML through the CMarkup colored glasses - you might miss that there's a lot more out there than you think.
Here's an example - one of the tasks I had to implement once was to transform an XML representation of a formula like structure into text. The developer who worked on the task before me already put in place C++ SAX based parsing solution, but at the time he did it the XML was quite simple and this worked fine. In the meantime the requirements changed and the new structure was quite a bit more complicated - note that there wasn't much of data, but its structure was involved. After struggling with C++ for a while, I noticed that the problem can be almost trivially solved with XSLT, so I did. I am sure that CMarkup would make the solution simpler than C++ SAX solution I had to start with, but just because XSLT is not well suited for certain transformation cases does not mean it is useless for other.
Yet another example is reading XML documents without a schema from C++. I am sure that you can get even malformed XML with CMarkup very fast but what would you do with it in case many elements contained optional attributes and sub-elements with default values? Litter the code with zillion ifs to check if something is there or not and then populate defaults? How would you check for deeper structural dependencies between elements and attributes? Ah, but you wouldn't - your XML documents would be trivially simple (in structure!) anyway.
CMarkup is great tool for a specific purpose, but might be horribly inadequate for another. While it helps those that have simple structured XML documents to deal with and just want to get the job done, it also supports the lazy among us developers who cannot be bothered with specs. This can sometimes result in developer thinking that “this whole XML thing” is really simple and start producing ill-formed and invalid XML. Here's an example of that - name one thing that is the hardest part of building an RSS/Atom reader? It's not following the standards of the RSS/Atom markup, it's struggling with all kinds of ill-formed markup that content producers spew out. If we'd all pay just a bit more attention to “boring” specs, the interoperability (that everyone seems to agree is the good thing) would be so much easier.
In conclusion, I don't think that the fact that CMarkup exists (and is quite useful in a special niche) invalidates specs for any XML technology. Right tool for the right job - sometimes this will mean (a bit) more work for developer.
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
Some time ago in my post Books on beta versions - is it really necessary I said that I did not understand the point in publishing books about beta versions of technologies. I understand that the motive is to get the technology into the hands of developers as soon as possible so by the time it ships they can be sort-of ready to start using it.
Well, I changed my stance on this if just a bit. The original rant was mainly about ASP.NET 1.1 being current (at the time) and ASP.NET 2.0 being the new technology. Note the important thing here - 2.0 is an incremental improvement over 1.1, it's not fundamentally different.
When a completely new technology is in question, it will take a really long time for many to adapt so having a book early even if the content is slightly inaccurate should be helpful. Recently I gave in the urge and bought a book about Avalon AKA Windows Presentation Foundation. I shamefully admit that the main driver was not the reputation of authors (they have it undoubtedly) but the very good deal on a few other books I got from Amazon USA (I normally order from European, mostly French site as the shipping is free and fast).
Avalon changes many things developers got used to over the last 20 years or so. Old habits die hard and if you plan on switching soon you should definitely start reading as much as possible. It's not just about the presentation, but about command routing, input and unified data-binding. Don't fear though - it's all logical, consistent and very clean. I find it a lot easier to author Avalon apps even without a GUI designer than to build WinForms apps, but that might just be me.
I am probably going to buy another book or two before Avalon ships because one of the authors - Charles Petzold - is very well known for his earlier work while another - Chris Anderson - is an architect on the Avalon team. At least Charles' book is supposed to come out slightly before if not to coincide with the final version. Judging by Chris' pace and his new hobbies I'd say his book will come out last :)
In short - books on beta versions are not useful if they are covering an existing technology's incremental improvement. If they are about a completely and fundamentally new technology, not only that it's smart to grab one as soon as possible, but you should probably get a few.
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
Another big obstacle for XSLT adoption is that it's hard to do groupings. Yet a lot of XML data comes not from the database (otherwise the right way to do groupings would be SQL anyway) but from some (un)known external source and needs to be grouped preferably during the transformation into a known structure.
In my earlier post about better visualizer for the XML output of the Visual Studio 2005 conversion wizard I mentioned how I used Muenchian grouping and Microsoft didn't, but I did explain exactly what the difference was. Besides grouping is just another great new addition to the XSLT 2.0 so I could not resist and reimplemented the core of the stylesheet (grouping part) in XSLT 2.0 in order to find out if it really is easier and/or better.
Here's the problem - some app (in this case Visual Studio 2005) outputs the XML that looks like this:
<UpgradeLog>
<Properties>
<Property Name="Solution" Value="My.Solution"/>
<Property Name="Solution File" Value="C:\My.Solution.sln"/>
<Property Name="User Options File" Value="C:\My.Solution.suo"/>
<Property Name="Date" Value="24. decembar 2005"/>
<Property Name="Time" Value="12:56"/>
</Properties>
<Event ErrorLevel="0" Project="" Source="My.Solution.sln" Description="File successfully backed up..."/>
<Event ErrorLevel="0" Project="" Source="My.Solution.suo" Description="File successfully backed up..."/>
<Event ErrorLevel="0" Project="My.AddIn" Source="My.AddIn.csproj" Description="Project file successfully backed up..."/>
<Event ErrorLevel="0" Project="My.AddIn" Source="My.AddIn.csproj.user" Description="Project user file successfully backed up..."/>
<Event ErrorLevel="0" Project="My.AddIn" Source="AmazonItemControl.cs" Description="File successfully backed up..."/>
<!-- rest of elements snipped -->
</UpgradeLog>
The job is to reorganize this into another XML that has more obvious structure - for each project (that is, Event with a distinct @Project), then for each source (again, distinct @Source inside each distinct project) list all the events. The output should look something like this:
<projects>
<project name="" solution="My.Solution">
<source name="My.Solution.sln">
<event error-level="0" description="File successfully backed up..."/>
<event error-level="0" description="Solution converted successfully"/>
<event error-level="3" description="Converted"/>
</source>
<source name="My.Solution.suo">
<event error-level="0" description="File successfully backed up..."/>
</source>
</project>
<!-- more project elements here -->
</projects>
Nothing special - we turn a flat list into a tree so that it's more obvious what belongs to where. (btw complete example files that demonstrate all this can be downloaded from here). Let's first look at Microsoft's implementation. I will ignore for the moment the detail where a final tree is built from two steps where first one just does sorting, but note that it's there and precedes what I am about to show. First the stylesheet - the discussion follows:
<xsl:key name="ProjectKey" match="Event" use="@Project"/>
<xsl:template match="Events" mode="createProjects">
<projects>
<xsl:for-each select="Event">
<xsl:if test="(1=position()) or (preceding-sibling::*[1]/@Project != @Project)">
<xsl:variable name="ProjectName" select="@Project"/>
<project>
<xsl:attribute name="name">
<xsl:value-of select="@Project"/>
</xsl:attribute>
<xsl:if test="@Project=''">
<xsl:attribute name="solution">
<xsl:value-of select="@Solution"/>
</xsl:attribute>
</xsl:if>
<xsl:for-each select="key('ProjectKey', $ProjectName)">
<xsl:if test="(1=position()) or (preceding-sibling::*[1]/@Source != @Source)">
<source>
<xsl:attribute name="name">
<xsl:value-of select="@Source"/>
</xsl:attribute>
<xsl:variable name="Source">
<xsl:value-of select="@Source"/>
</xsl:variable>
<xsl:for-each select="key('ProjectKey', $ProjectName)[ @Source = $Source ]">
<event>
<xsl:attribute name="error-level">
<xsl:value-of select="@ErrorLevel"/>
</xsl:attribute>
<xsl:attribute name="description">
<xsl:value-of select="@Description"/>
</xsl:attribute>
</event>
</xsl:for-each>
</source>
</xsl:if>
</xsl:for-each>
</project>
</xsl:if>
</xsl:for-each>
</projects>
</xsl:template>
Outer loop goes for-each Event element, then uses funky XPath expression to get rid of duplicate projects thus simulating the first grouping level. Then it uses the key to filter out all projects with the same name as the one in the outer loop, only to do the same XPath expression this time getting rid of duplicate sources thus simulating second level grouping. Final inner loop just filters all elements with the project and source attributes picked up from the outer loops.
Not really elegant IMHO, but relatively readable assuming you are comfortable with XPath. It also looks a bit suboptimal, but more on performance later. Let's have a look at my (slightly revised from the original post) version using XSLT 1.0:
<xsl:key name="events-by-project" match="Event" use="@Project"/>
<xsl:key name="events-by-source" match="Event" use="@Source"/>
<xsl:template match="UpgradeLog">
<projects>
<xsl:variable name="Projects"
select="Event[count(. | key('events-by-project', @Project)[1]) = 1]"/>
<xsl:for-each select="$Projects">
<xsl:sort select="@Project" order="ascending"/>
<project>
<xsl:variable name="prj" select="@Project"/>
<xsl:attribute name="name">
<xsl:value-of select="$prj"/>
</xsl:attribute>
<xsl:if test="$prj =''">
<xsl:attribute name="solution">
<xsl:value-of select="/UpgradeLog/Properties/Property[@Name = 'Solution']/@Value"/>
</xsl:attribute>
</xsl:if>
<xsl:variable name="Sources"
select="../Event[@Project = $prj and count(. | key('events-by-source', @Source)[1]) = 1]"/>
<xsl:for-each select="$Sources">
<xsl:sort select="@Source" order="ascending"/>
<source>
<xsl:variable name="src" select="@Source"/>
<xsl:attribute name="name">
<xsl:value-of select="@Source"/>
</xsl:attribute>
<xsl:variable name="Descriptions" select="../Event[@Project = $prj and @Source = $src]"/>
<xsl:for-each select="$Descriptions">
<xsl:sort select="@ErrorLevel" order="ascending"/>
<event>
<xsl:attribute name="error-level">
<xsl:value-of select="@ErrorLevel"/>
</xsl:attribute>
<xsl:attribute name="description">
<xsl:value-of select="@Description"/>
</xsl:attribute>
</event>
</xsl:for-each>
</source>
</xsl:for-each>
</project>
</xsl:for-each>
</projects>
</xsl:template>
It's not that much shorter, isn't it? Nevertheless, it should at least be more readable, assuming you know about Muenchian grouping. Looking at the usage of keys, it should also be more optimal. Let's see - first level is classic Muenchian - based on a key, I filter all out distinct Event nodes per project. Then I use the same technique grouping by source and filter by the project picked from the outer loop. Finally the most inner loop is more or less the same as Microsoft's. Note that I do sorting along the way while Microsoft did it in a step I did not show.
At last, let's have a look how this same thing looks like in XSLT 2.0 (tried with both AltovaXML and SaxonB):
<xsl:template match="UpgradeLog">
<projects>
<xsl:for-each-group select="Event" group-by="@Project">
<xsl:sort select="@Project"/>
<project>
<xsl:attribute name="name" select="@Project"/>
<xsl:if test="@Project = ''">
<xsl:attribute name="solution"
select="/UpgradeLog/Properties/Property[@Name='Solution']/@Value"/>
</xsl:if>
<xsl:for-each-group select="current-group()" group-by="@Source">
<xsl:sort select="@Source"/>
<source>
<xsl:attribute name="name" select="@Source"/>
<xsl:for-each select="current-group()">
<xsl:sort select="@ErrorLevel"/>
<event>
<xsl:attribute name="error-level" select="@ErrorLevel"/>
<xsl:attribute name="description" select="@Description"/>
</event>
</xsl:for-each>
</source>
</xsl:for-each-group>
</project>
</xsl:for-each-group>
</projects>
</xsl:template>
Aha - that's more like it. Not only that it is shorter overall, it's a lot easier to read. Using a new for-each-group construct, it's a lot more obvious I am doing grouping here. Note that the code is visibly shorter also due to a small but important difference - value for xsl:attribute can be specified directly in the select attribute and we have quite a few of these.
What about speed and memory consumption? Turns out it's more or less the same across all three stylesheets, except that my 1.0 version needs a bit more memory. What's important to note is that while 2.0 version is by far the cleanest and the easiest to read (which to me is the most important quality here) you do not have to sacrify the speed nor the memory as a consequence - 2.0 version was consistently the fastest and used least memory (even if by a very small margin on both accounts).
If some of the XSLT 1.0 aspects frustrate you, try XSLT 2.0. You might be very pleasantly surprised. While Altova provides a lot better IDE in the shape of XMLSpy Home edition their XSLT processor is not as complete as Saxonica's, so I definitely recommend the latter. If you'd like to try some of the schema related features though (hopefully I'll write about that soon) then Altova's package is a better deal - it's free while Saxonica's solution is commercial.
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
I guess many have been turned off of XSLT just because of this one simple thing - trivial looping is
very hard. By trivial looping I mean something like this (Ruby):
# simple
(1..200).each { |i| puts "<something index=\"#{i}\"/>" }
# more verbose
for i in 1..200
puts "<something index=\"#{i}\"/>"
end
# output is <something index=“1“/> ... <something index=“200“/>
Of course, you'd never generate XML this way :) but it shows a simple way to loop from 1 to 200. How hard is this in XSLT 1? Let's see (XSLT 1.0):
<xsl:template
match="/"> <xsl:call-template name="iterate">
<xsl:with-param name="i" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="iterate">
<xsl:param name="i"/>
<xsl:if test="$i <= 200">
<something>
<xsl:attribute name="index">
<xsl:value-of select="$i"/>
</xsl:attribute>
</something>
<xsl:call-template name="iterate">
<xsl:with-param name="i" select="$i + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Whoa, that's a lot of lines! We're simulating iteration by recursion* which is not only inefficient but confusing for beginners. Also, quite a few lines of code to chew. All that for a simple loop?!?
Here's the same loop with the new XSLT, currently W3C candidate recommendation (XSLT 2.0):
<xsl:for-each
select="1 to 300"> <something>
<xsl:attribute name="index" select="." />
</something>
</xsl:for-each>
That's more like it - clean and simple. Apart from sequences (1 to 300) you can also see one other small improvement here - I was able to use select attribute on an xsl:attribute.
XSLT 2.0 is not only better for experienced coders, but for beginners too. Even though many will be lured by even greater simplicity (and likeness to "regular" languages) of XQuery, the language for XML processing will still be XSLT.
*Because of recursion there is a XSLT processor imposed limit on the iteration depth which seems to be about 3400 entries for Altova's XML processor - after that the call stack overflow. Saxon seems to be better in this regard but should have a limit too unless internally this pattern is recognized and manually optimized to avoid recursion, which I doubt.
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
Some time ago I complained about Google desktop search. It's installation sucked so much I was not able to install it.
The other contender from a big software company came some time after that - Microsoft launched Windows Desktop Search. I decided to give it a try; after all, which company could achieve better OS integration than Microsoft? I have also heard about IFilter interface that should be a simple way to allow developers to open their custom documents up to the indexer.
After using it for about 4 months, I gave up. The app is a memory hog. It is not obtrusive in that it waits for your inactivity to do the indexing, but even when idle it consumes non-trivial amounts of memory. I would have ignored this if the results of search came quickly and were easy to read. Neither were satisfactory though - search would take several seconds longer than you'd expect and the result were very unreadable - despite the setting to highlight the words searched for inside the document you're looking at this did not work. On top of all this the result viewer felt very slow and the results were always sorted by some magical and totally unusable method (instead of by relevance).
But there is one desktop search app that is worth your attention: Copernic Desktop Search. You might remember Copernic from before when they had various Internet search related products. You get basically the same package as with other desktop search products except that this one works well. Indexing is unobtrusive, search is fast and all the result keywords are clearly highlighted on each and every matched document. It indexes the same kind of documents like all the competitors and actually has better extensibility model for the .NET developers than Microsoft! Highly recommended.
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
While we're waiting for the XSLT 2.0 compliant implementations we can still use good ol' XSLT 1.0, especially with the help of the XSLT support in the .NET Framework 2.0 and Visual Studio 2005.
If you've tried to upgrade a project from version 2003 to 2005 you must have noticed that once the conversion wizard is done you are offered to have a look at the results. The result is just a simple XML file (named UpgradeLog.xml) that has XSLT stylesheet referenced thus when opened from Internet Explorer you get a nicely laid out HTML page.
The XSLT file used can be found in the Visual Studio installation folder - it's in /Common7/Ide/1033. On each conversion this file and associated CSS and 2 GIF files get copied into the solution folder and are referenced from the resulting HTML file. XSLT file can be replaced with your own implementation, but the CSS and GIF files are nowhere to be found. On top of all this, if you open the UpgradeLog.xml file from Firefox, you will get a following error: Error during XSLT transformation: An unknown XPath extension function was called.
The problem here is that Firefox does not support node-set extension function that is practically mandatory extension for any serious XSLT 1.0 processor (and is one of the reasons XSLT 2.0 would be really nice to have). Another problem is that CSS and 2 GIF files are not present in the filesystem so if you want to customize or completely replace the XSLT you need to refactor it so that it either uses the same styles and GIF files, or rewrite it completely.
Now, why would you want to do that? Well, at least for me, the resulting HTML is not really readable. The most important information - whether conversion succeeded or not and where it failed - is not at all obvious from the output. So I decided to write my own and to make it usable from Firefox too :)
The result is the stylesheet that you can download from here. Just copy it over the default one and enjoy (don't forget to back up the original!). Here's the output from my stylesheet:
As you can see, several things are immediately noticeable - at the very top, there is a reddish circle by the report name - if there were no warnings, this would be a green check-mark (and if there were errors it would be a red cross). For each of the successful conversions you can see green text (whether file was converted or not), otherwise it is reddish when there are warnings and red when there are errors. At the top, you can also see an overall count of errors and warnings.
All in all, just a quick look is enough to verify if things went OK or not. As is also obvious, the conversion now works from Firefox. It doesn't hurt that the XSLT is now self-sufficient and half of the size of Microsoft's implementation :) But that's not all - it should be faster too (not that it matters in this case, though)...
So how did I do it? For a start, I avoided using the node-set extension function. There's nothing wrong with this function - in fact mostly it's crucial to effective XSLT script - but in this case it was not necessary. The main reason Microsoft used it is because the input XML tree was first transformed (twice!) into an intermediary representation in order to simplify the final transform phase. However this could have been avoided due to the reason why all this was done - just to do a relatively simple two-level grouping of nodes.
There is a well-known trick to achieve grouping in XSLT called Muenchian grouping. It is based on the usage of less-known XSLT construct called key. It was still a bit of work to get it right here because the data requires two-level grouping, while most if not all of the examples of xsl:key usage demonstrate simple one-level grouping. I decided to put the result of the first level grouping in a variable and then group over that in exactly the same way. It worked like a charm - the cool thing is that putting nodes in a variable will not require using a node-set afterward to traverse the nodes.
I have also decided to use CSS styling instead of GIF files for the expand/collapse “buttons”. The result is small, efficient and self-sufficient XSLT script that should work in all standard compliant browsers (tested in Firefox and Internet Explorer 6). Enjoy!
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
A recent post XSLT 2.0 and Microsoft from Dare Obasanjo provoked quite a reaction - some people (me included) would obviously like to see Microsoft develop XSLT 2 processor as soon as possible. The specification should enter a W3C “recommended” status in a few months, so the time is right.
Considering the great XSLT 1.0-to-IL compiler Microsoft gave us with the .NET Framework 2.0, I have high hopes for the XSLT 2.0 on the .NET platform. But it's gonna take some time until we see this - at best we can hope for it in the time frame of the next Visual Studio assuming it will coincide with the next .NET Framework version (XSLT processor belongs to the framework and not the IDE).
In the meantime, you have few alternatives. Both Saxonica (Michael Kay's company, he's probably the authority on XSLT and the editor of W3C XSLT 2.o group) and Altova (company behind arguably the best XML tools out there) have compliant implementations. Since XSLT processor is the main business for Saxonica they only offer non schema-aware version for free. And it's Java, so it's not directly usable from a .NET application.
Altova however sells many XML tools (XMLSpy and the family) so for them it is not a big deal to give away the processor - it should raise the brand awareness and sell more copies of their tools. Therefore they are giving away schema-aware XSLT 2.0 processor complete with XQuery 1.0 and XPath 2.0! Even better - it's COM based and directly usable from any .NET application.
I have tried both implementations with some of the Zvon's tutorials (great XML related site if you haven't seen it yet) and it looks like Saxon-B (the free version I mentioned above) is definitely better - some of the simple cases AltovaXML did not transform correctly. I will have to throw more complicated stylesheets at both to draw any conclusion but the apps are free and can be used today. You don't even have to use command line versions of AltovaXML - they are giving away free (scaled down of course) XMLSpy version for home use.
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
A few years ago my wife and I moved to France. We live in a small town on the coast and don't have kids (yet). So when time came to choose a car, I picked Peugeot 206. Small(ish), but enough for the two of us and easy to park in the crowded streets of Antibes-Juan Les Pins.
I like luxury (and my wife adores it ;) ) so I took practically all extra equipment (sans GPS and sunroof) - we got automatic air-conditioning, automatic wipers, automatic lights, car complains audibly for most of the small things like door left open, hand brake is on etc. I've been pretty happy about the choice for the past three years or so.
My wife recently got her driving license - she got tired of asking me to drive her everywhere or to organize car pooling with my colleagues so that the wives can go somewhere together. And now we have a problem - each time one of us uses the car after the other, we have to set up all the mirrors, height and distance of the driver seat and if it made any difference, we'd probably adjust the height of the steering wheel too.
As you might know (or not) more expensive car models include the feature where all these settings can be saved in a driver profile and recalled at any time. It's like software - there are users and roles and per-user settings.
Some might call this feature bloat, but I'd pay for this serious money. It gets really annoying and the only alternative is even worse - to buy another car. But it's too late now - if I want this I have to buy another car.
So next time you think how the app you're using is “bloated” think of this - one day you might need that “stupid” feature. If it doesn't slow the app down and does not waste memory or disk space - be thankful; you never know, you might need it sooner than later.
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5