views:

143

answers:

4

I'm working with an OpenXML document, processing the main document part with some XSLT.

I've selected a set of nodes via

<xsl:template match="w:sdt">
</xsl:template>

In most cases, I simply need to replace that matched node with something else, and that works fine.

BUT, in some cases, I need to replace not the w:sdt node that matched, but the closest w:p ancestor node (ie the first paragraph node that contains the sdt node).

The trick is that the condition used to decide one or the other is based on data derived from the attributes of the sdt node, so I can't use a typical xslt xpath filter.

I'm trying to do something like this

<xsl:template match="w:sdt">
  <xsl:choose>
    <xsl:when test={first condition}> 
        {apply whatever templating is necessary}
    </xsl:when> 
    <xsl:when test={exception condition}> 
      <!-- select the parent of the ancestor w:p nodes and apply the appropriate templates -->
      <xsl:apply-templates select="(ancestor::w:p)/.." mode="backout" />
    </xsl:when> 
  </xsl:choose> 
</xsl:template>


<!-- by using "mode", only this template will be applied to those matching nodes
     from the apply-templates above -->
<xsl:template match="node()" mode="backout">
  {CUSTOM FORMAT the node appropriately}
</xsl:template>

This whole concept works, BUT no matter what I've tried, It always applies the formatting from the CUSTOM FORMAT template to the w:p node, NOT it's parent node.

It's almost as if you can't reference a parent from a matching node. And maybe you can't, but I haven't found any docs that say you can't

Any ideas?

A: 

How about parent::* or simply ..?

lexicore
A: 

If you have already processed the w:p node, you cannot backtrack when you encounter the descendant w:sdt node and replace the processing done for the ancestor. You need to determine whether or not to do custom formatting when processing the w:p node itself in the first place.

One way to do this is to override your template for w:p nodes so that you have

  • a general template for w:p nodes
  • an overriding template for w:p nodes that are the closest ancestor of a special case w:sdt node

To determine whether a w:p is a closest ancestor or not, you can use xsl:key.

Example:

<xsl:key name="sdt-descendants" 
         match="w:sdt[@someAttribute='someValue']"
         use="generate-id(ancestor::w:p[1])"/>

<xsl:template match="w:p">
    <!-- General behavior -->
</xsl:template>

<xsl:template match="w:p[key('sdt-descendants', generate-id())]">
    <!-- Specific behavior if the element is the closest w:p ancestor to a 
         descendant w:sdt element matching the provided criteria. -->
</xsl:template>

The second template will be used for all w:p elements that are the closest ancestor to w:sdt elements with the specified attributes, and the first template will be used for all other w:p elements.

markusk
A: 

The approach to process the parent after its child is incorrect for an XSLT application.

Please, provide a working (but smallest possible) example, containing the source XML document and the actual xslt stylesheet. Also, explain what output should be produced and how the output is derived from the source XML document.

This said, the parent of the current node is selected by this simple XPath expression:

..
Dimitre Novatchev
A: 

This:

<xsl:apply-templates select="(ancestor::w:p)/.." mode="backout" />

will find all w:p elements that are ancestors of the context node, and apply templates to the parent elements of each. It sounds to me like maybe what you want to do is find only the nearest ancestor, e.g.:

<xsl:apply-templates select="ancestor::w:p[1]/.." mode="backout" />

But what you're describing here should be working, in some fashion. You should probably verify that what you think is happening is actually what's happening, by replacing your backout template with something more diagnostic, e.g.:

<xsl:template match="node()" mode="backout">
   <xsl:text>backout matched a </xsl:text>
   <xsl:value-of select="name()"/>
   <xsl:text> element.</xsl:text>
</xsl:template>
Robert Rossney
Looks like this was the trick. Thanks
drventure