40275

Transforming transformed XML with XSL?

Question:

Let's say I have some XSL transforming XML. Is it possible for the same XSL sheet to run a second sweep over the resultant XML? For example, let's say my XSL turns

<xml> <animal><dog>Rex</dog></animal> <animal><dog>Henry</dog></animal> <animal><dog>Fido</dog></animal> </xml>

into

<xml> <dog>Rex</dog> <dog>Henry</dog> <dog>Fido</dog> </xml>

I don't want to output that; rather, I then want to perform more XSL based on THAT, i.e. resultant, XML structure.

A practical example? I want to append to each dog node the number of proceeding dog siblings it has. So it would end up like:

<xml> <dog>Rex (2)</dog> <dog>Henry (1)</dog> <dog>Fido (0)</dog> </xml>

This could not be done on the first sweep because in the beginning XML, the dog nodes were not siblings - they each lived inside an animal node.

[<strong>EDIT</strong>: I know this <em>could</em> be done by interrogating instead the index of the parent animal node, but that's only for this contrived example; generally, I still need to know how to act on transformed XML - if it's even possible]

I hope that makes some sort of sense. If there is a really easy way to do this, go easy on me, as I'm no XSL ninja...

Thanks in advance

Answer1:

You can use modes on templates to process nodes more than one time but with different transformations. So you could do e.g.

<xsl:variable name="result1"> <xsl:apply-templates select="xml" mode="m1"/> </xsl:variable> <xsl:variable name="result2"> <xsl:apply-templates select="$result1/xml" mode="m2"/> </xsl:variable> <xsl:template match="/"> <xsl:copy-of select="$result2"/> </xsl:template> <!-- now put templates for the modes here -->

That above is fine only with XSLT 2.0 however, with XSLT 1.0 you have the drawback that a temporary result is a result tree fragment which you need to convert into a node-set first with an extension function like <a href="http://www.exslt.org/exsl/functions/node-set/index.html" rel="nofollow">exsl:node-set</a> so with XSLT 1.0 you need e.g.

<xsl:variable name="result1"> <xsl:apply-templates select="xml" mode="m1"/> </xsl:variable> <xsl:variable name="result2"> <xsl:apply-templates select="exsl:node-set($result1)/xml" mode="m2"/> </xsl:variable> <xsl:template match="/"> <xsl:copy-of select="$result2"/> </xsl:template> <!-- now put templates for the modes here -->

Answer2:

Yes, breaking up a complex transformation into a sequence of simple transformations is a standard design pattern for XSLT and is highly recommended.

There are two ways of doing it: single-stylesheet and multiple-stylesheet. In a single-stylesheet pipeline you generally use modes, one mode for each phase of the transformation. The intermediate results are held in variables (which means that in XSLT 1.0, you need to use xx:node-set() to make the result of one phase available for processing in the next phase).

Using multiple stylesheets - a sequence of transformations each with its own XSLT stylesheet - is a bit more work to set up because it needs some control mechanism to run the pipeline. But in the end it's often better because the code is less complex and more reusable. There are a number of technologies you can use to control the pipeline - Ant, XProc, shell scripts, xmlsh, Orbeon, Coccoon, or plain old Java.

Recommend

  • How can I prevent random numeric code changes in Visual Basic?
  • preg_replace_callback pattern issue
  • Clang and CMake on Windows
  • build clang from source using specific gcc toolchain
  • can't get the image to rotate in center in Qt
  • Getting “node stack overflow” when cbind multiple sparse matrices
  • XslTransform with xml-stylesheet
  • Why async.map function works with the native fs.stat function?
  • How can I organize this data into the objects I want with LINQ?
  • in batch how do i use taskkill properly
  • Retrieving specified columns from a list of csv files to create a data data frame in R
  • Can't remove headers after they are sent
  • Clear activity stack before launching another activity
  • Filter strings with regex before casting to numeric
  • Android changing fragment order inside FragmentPagerAdapter
  • Jquery popup on mouse over of calendar control
  • C# program and C++ DLL compiled for 32-bit system crash on 64-bit system
  • Blackberry - Custom EditField Cursor
  • How can I sort a a table with VBA with given text condition?
  • How do I get HTML corresponding to current DOM tree?
  • Android full screen on only one activity?
  • Refering to the class itself from within a class mehod in Objective C
  • How to create a file in java without a extension
  • Is it possible to access block's scope in method?
  • Meteor helpers not available in Angular template
  • Django: Count of Group Elements
  • formatting the colorbar ticklabels with SymLogNorm normalization in matplotlib
  • PHPUnit_Framework_TestCase class is not available. Fix… - Makegood , Eclipse
  • Fetching methods from BroadcastReceiver to update UI
  • Get object from AWS S3 as a stream
  • Symfony2: How to get request parameter
  • Rearranging Cells in UITableView Bug & Saving Changes
  • GridView Sorting works once only
  • R: gsub and capture
  • WPF Applying a trigger on binding failure
  • Proper way to use connect-multiparty with express.js?
  • Benchmarking RAM performance - UWP and C#
  • Does armcc optimizes non-volatile variables with -O0?
  • Conditional In-Line CSS for IE and Others?
  • Net Present Value in Excel for Grouped Recurring CF