| Subject: [xsl] inserting a child element while honoring the parent  element's content model From: "Chris Papademetrious christopher.papademetrious@xxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> Date: Sun, 19 Feb 2023 16:30:21 -0000 | 
Hi everyone,
I needed to insert a child element while honoring the parent element's content
model. The solution took me some time to figure out, so I thought I'd share it
here.
Consider a <topic> element with the following content model:
topic = a?, b?, c?, j?, x?, y?, z?, topic*
Given the following input document with no <j> elements:
<?xml version="1.0" encoding="utf-8"?>
<topic>
  <!-- this is a comment -->
  <a/>
  <?my-a-pi?>
  <b/>
  <y/>
  <topic>
    <!-- y -->
    <y/>
  </topic>
  <topic>
    <!-- c -->
    <c/>
  </topic>
</topic>
I want to insert <j> into every <topic> element. To do this, I apply the
following general template that calls an "add-j" moded template on every
<topic> element lacking a <j>:
  <xsl:mode on-no-match="shallow-copy"/>
  <xsl:template match="topic[not(j)]">
    <xsl:variable name="result" as="element()">
      <xsl:apply-templates select="." mode="add-j"/>
    </xsl:variable>
    <xsl:apply-templates select="$result"/>
  </xsl:template>
Then I define the "add-j" mode as follows:
  <!-- add <j> to <topic>, honoring the following content model:
         topic = a?, b?, c?, j, x?, y?, z?, topic* -->
 <xsl:template match="topic[not(j)]" mode="add-j">
    <xsl:variable name="stuff-before-j"
select="(a|b|c)/(.|preceding-sibling::node())" as="node()*"/>
    <xsl:copy>
      <xsl:sequence select="@*|$stuff-before-j"/>
      <j/>
      <xsl:sequence select="node() except $stuff-before-j"/>
    </xsl:copy>
  </xsl:template>
The tricky part was obtaining $stuff-before-j, since I wanted a
"preceding-or-self::" axis to retain any PIs or comments in their correct
place. Once I got that figured out, the rest fell into place.
The reason for a separate "add-j" moded template is that in my full
stylesheet, I have many templates doing things in many different places, and
so I use moded templates and <xsl:apply-templates/> so that everything plays
well together.
The solution works in either direction (stuff-before or stuff-after). In our
case, we are using DITA and the topics at the end of the content model can be
other specialized elements, so using the "stuff-before" flavor lets me match
any specialized topic that might follow.
I have much uglier implementations of content-model-aware insertion in past
stylesheets that I'll need to convert to this form.
  *   Chris
-----
Chris Papademetrious
Tech Writer, Implementation Group
(610) 628-9718 home office
(570) 460-6078 cell
| Current Thread | 
|---|
| 
 | 
| <- Previous | Index | Next -> | 
|---|---|---|
| [no subject], Unknown | Thread | Re: [xsl] inserting a child element, Eliot Kimber eliot.k | 
| [no subject], Unknown | Date | [xsl] How to 'execute' a table with, Roger L Costello cos | 
| Month |