Transforming transformed XML with XSL?

2019-08-07 14:26发布

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.

[EDIT: I know this could 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

标签: xml xslt
2条回答
萌系小妹纸
2楼-- · 2019-08-07 14:54

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 exsl:node-set 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 -->
查看更多
一夜七次
3楼-- · 2019-08-07 15:01

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.

查看更多
登录 后发表回答