I dont know that its a better solution, but I often use milestone nodes
and the << and >> operators to position things:
<xsl:variable name=fromHere as=element() select=p[1]/>
<xsl:variable name=beforeParaNodes as=node()* select=node()[. <<
$fromHere]/>
I find this especially handy for getting nodes between two variable points,
like all nodes before me that are preceding siblings or descendants of the
first child of my parentthis is a challenge that comes up in DITA with some
frequency because of the rules around ID uniqueness within topics, for which
theres no easy single container.
Cheers,
E.
_____________________________________________
Eliot Kimber
Sr Staff Content Engineer
O: 512 554 9368
M: 512 554 9368
servicenow.com<https://www.servicenow.com>
LinkedIn<https://www.linkedin.com/company/servicenow> |
Twitter<https://twitter.com/servicenow> |
YouTube<https://www.youtube.com/user/servicenowinc> |
Facebook<https://www.facebook.com/servicenow>
From: Chris Papademetrious christopher.papademetrious@xxxxxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Sunday, February 19, 2023 at 10:37 AM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx <xsl-list@xxxxxxxxxxxxxxxxxxxxxx>
Subject: inserting a child element while honoring the parent element's
content model
[External Email]
________________________________
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 Id 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 Ill need to convert to this form.
* Chris
-----
Chris Papademetrious
Tech Writer, Implementation Group
(610) 628-9718 home office
(570) 460-6078 cell
XSL-List info and archive<http://www.mulberrytech.com/xsl/xsl-list>
EasyUnsubscribe<http://lists.mulberrytech.com/unsub/xsl-list/3453418> (by
email<>)
|