Saturday, May 14, 2011

Stupid XSLT Tricks

Found this post in my drafts folder. I had intended to post it after I had tested the technique. I have used it now (with a few tweaks), and it works. You might need to fiddle with it yourself a bit. It's another of those idioms I'll keep in my bag of tricks.


This is another one of what I call stupid XSLT tricks.  Stupid because programming should be clear and simple, and this is NOT.  Tricky and worth writing about because it does something useful.

I'm trying to line up two sets of assertions (in Schematron).  I have a list of template identifiers (in an XSLT node-set), and I want to find all Schematron rules (and assertions) that apply to that template.

Fortunately, I can pretty much expect that one of the expressions similar to the following will be present in the context of the rule.

*[cda:templateId/@root="templateId"]
*[cda:templateId/@root='templateId']
* [ cda:templateId / @ root = "templateId" ]
* [ cda:templateId / @ root = 'templateId' ]

What I cannot count on is consistency in use of white space or quote delimiters.  So, if I remove all the white space (using translate) , and the quote delimiters (using translate as well), what I'm left with is this:

*[cda:templateId/@root=templateId]

Just to simplify a little further (because there are quite a few variations on * as well), I decided to take whatever string was after cda:templateId/@root=, (using substring-after to give me templateId], and whatever in that appeared before the ] (using substring-before give me templateId). Now, if one or the other of those strings don't appear, I'm left with an empty string. I can now take my node-list containing template identifiers and turn it into a string in this form:

{templateId1}{templateId2}

I can now check to see if the templateId I found is in the list of templateIds I produced by wrapping it in a pair of delimiters (I used { and } ) using concat.

So this is what the XPath expression to find a list of templates looks like:

$ruleDoc//sch:rule[
contains(string($tlist), 
concat('{',substring-before(    
      substring-after(
        translate(translate(@context,'"'',' '),' ',''),
        "cda:templateId/@root="
       ),
']'
),
'}'
)
  )
]/sch:assert

Why was this important?  Because I want to process the assertions in an XSLT for-each  loop storing by the first cda:elementName or @attribute in the @test attribute of the Schematron assertion.

That way, when I look at a template in the CDA Consolidation project, and in the other templates, I'll be able to quickly find tests on the same elements within the two different sets of templates.

This is challenging work, something that I usually enjoy.

0 comments:

Post a Comment