update depth?

Monday, 27 April 2026 · 5 messages · last reply 28 Apr 2026
GS
Graydon Saunders27 Apr 2026 23:27 #
Hello! If I try to get rid of nested inline markup with no attributes
let $test as element() := <p>First <span><span><span>word</span></span></span> was excited.</p>
return $test update {(.//span[empty(@*)]) ! (replace node . with ./node()) }
I get
<p>First <span><span>word</span></span> was excited.</p>
Which tells me (or at least I think it tells me) that the outermost replace happens last and the result is replacing the outer span with its node children. I'm not getting free recursion by matching all the span elements. What I want is
<p>First word was excited.</p>
where all the span elements with no associated attributes unwrap. Is there a plausibly elegant way to do this? (Stacking the same update several times works, and a fully recursive typeswitch approach works, but I can't avoid thinking there's a better way.) Thanks! Graydon
-- Graydon Saunders | graydonish at fastmail.com Þæs oferéode, ðisses swá mæg. -- Deor ("That passed, so may this.")
LQ
Liam R. E. Quin28 Apr 2026 08:46 #
On Mon, 27 Apr 2026 17:26:25 -0400 Graydon Saunders via BaseX-Talk <basex-talk at mailman.uni-konstanz.de>
Quoted text · Graydon Saunders · 4 lines
wrote: > let $test as element() := <p>First > <span><span><span>word</span></span></span> was excited.</p> return > $test update {(.//span[empty(@*)]) ! (replace node . with ./node()) }
Here the first item will be the first span, and it'll be replaced with its contents, i.e. node(), which is a span element that in turn contains a span element. You need to replace with the result of replacing, but that indeed means recursion. Strictly speaking this approach isn't possible with XQuery Update, as an expression can't see the result of an update, but everyone hated that restriction and i doubt BaseX enforces it. Sometimes i use a "visitor" module to simulate xsl:apply-templates for this sort of change, although calling out to XSLT via transform() would work too, as would the recursive function with typeswitch you already wrote :) liam
-- Liam Quin: Delightful Computing - Training and Consultancy in XSLT / XML Markup / Typography / CSS / Accessibility / and more... Outreach for the GNU Image Manipulation Program Vintage art digital files - fromoldbooks.org
CG
Christian Grünin reply to Graydon Saunders, 27 Apr 2026, 23:2728 Apr 2026 14:07 #
Hi Graydon, In general, as Liam has already outlined, all updates refer to the original node. Your specific question could be answered with…
$test update { .//span[empty(@*)] ! (replace node . with .//text()) }
…but I guess this is not what you need. Instead of a recursive solution, fn:while-do can be used to do repeated updates:
while-do(
  $test,
  fn($r) { exists($r//span[empty(@*)]) },
  fn($r) { $r update { .//span[empty(@*)] ! (replace node . with ./node()) } }
)
Hope this helps, Christian
Quoted text · Graydon Saunders · 22 lines
Von: Graydon Saunders via BaseX-Talk <basex-talk at mailman.uni-konstanz.de> Gesendet: Montag, 27. April 2026 23:26 An: BaseX Betreff: [basex-talk] update depth? Hello! If I try to get rid of nested inline markup with no attributes let $test as element() := <p>First <span><span><span>word</span></span></span> was excited.</p> return $test update {(.//span[empty(@*)]) ! (replace node . with ./node()) } I get <p>First <span><span>word</span></span> was excited.</p> Which tells me (or at least I think it tells me) that the outermost replace happens last and the result is replacing the outer span with its node children. I'm not getting free recursion by matching all the span elements. What I want is <p>First word was excited.</p> where all the span elements with no associated attributes unwrap. Is there a plausibly elegant way to do this? (Stacking the same update several times works, and a fully recursive typeswitch approach works, but I can't avoid thinking there's a better way.) Thanks! Graydon -- Graydon Saunders | graydonish at fastmail.com<mailto:graydonish at fastmail.com> Þæs oferéode, ðisses swá mæg. -- Deor ("That passed, so may this.")
GS
Graydon Saunders28 Apr 2026 14:16 #
Hi Christian, If I replace with text(), I might lose a descendant span that has a class; <span><span class="something"><span>words</span></span></span> has to turn into <span class="something">words</span>. (I'm writing tests to count the words before and after for a conversion process, and we're getting cases where it's <span><span>S</span>pecial word</span> in the source and naive word counting gives both `S` and `pecial` as false positives.) fn:while-do looks like a good way to keep all the "adjusting the source" code in one visual location; thank you! -- Graydon
Quoted text · Christian Grün · 57 lines
On Tue, Apr 28, 2026, at 08:07, Christian Grün wrote: > Hi Graydon, > > In general, as Liam has already outlined, all updates refer to the original node. Your specific question could be answered with… > > $test update { .//span[empty(@*)] ! (replace node . with .//text()) } > > …but I guess this is not what you need. > > Instead of a recursive solution, fn:while-do can be used to do repeated updates: > > while-do( > $test, > fn($r) { exists($r//span[empty(@*)]) }, > fn($r) { $r update { .//span[empty(@*)] ! (replace node . with ./node()) } } > ) > > Hope this helps, > Christian > ________________________________________ > > Von: Graydon Saunders via BaseX-Talk <basex-talk at mailman.uni-konstanz.de> > Gesendet: Montag, 27. April 2026 23:26 > An: BaseX > Betreff: [basex-talk] update depth? > > Hello! > > If I try to get rid of nested inline markup with no attributes > > let $test as element() := <p>First <span><span><span>word</span></span></span> was excited.</p> > return $test update {(.//span[empty(@*)]) ! (replace node . with ./node()) } > > I get > > <p>First <span><span>word</span></span> was excited.</p> > > Which tells me (or at least I think it tells me) that the outermost replace happens last and the result is replacing the outer span with its node children. I'm not getting free recursion by matching all the span elements. > > What I want is > > <p>First word was excited.</p> > > where all the span elements with no associated attributes unwrap. > > Is there a plausibly elegant way to do this? > > (Stacking the same update several times works, and a fully recursive typeswitch approach works, but I can't avoid thinking there's a better way.) > > Thanks! > Graydon > -- > Graydon Saunders | graydonish at fastmail.com<mailto:graydonish at fastmail.com> > Þæs oferéode, ðisses swá mæg. > -- Deor ("That passed, so may this.") > >
GS
Graydon Saundersin reply to Liam R. E. Quin, 28 Apr 2026, 08:4628 Apr 2026 14:21 #
Thanks, Liam. Appreciate the confirmation that I wasn't missing an option. Going to go try do-until and then typeswitch if I haven't got the brain cells for do-until.
Quoted text · Liam R. E. Quin · 34 lines
On Tue, Apr 28, 2026, at 02:46, Liam R. E. Quin wrote: > On Mon, 27 Apr 2026 17:26:25 -0400 > Graydon Saunders via BaseX-Talk <basex-talk at mailman.uni-konstanz.de> > wrote: > > > let $test as element() := <p>First > > <span><span><span>word</span></span></span> was excited.</p> return > > $test update {(.//span[empty(@*)]) ! (replace node . with ./node()) } > > Here the first item will be the first span, and it'll be replaced with > its contents, i.e. node(), which is a span element that in turn contains > a span element. > > You need to replace with the result of replacing, but that indeed means > recursion. > > Strictly speaking this approach isn't possible with XQuery Update, as > an expression can't see the result of an update, but everyone hated > that restriction and i doubt BaseX enforces it. > > Sometimes i use a "visitor" module to simulate xsl:apply-templates for > this sort of change, although calling out to XSLT via transform() would > work too, as would the recursive function with typeswitch you already > wrote :) > > > liam > > -- > Liam Quin: Delightful Computing - Training and Consultancy in > XSLT / XML Markup / Typography / CSS / Accessibility / and more... > Outreach for the GNU Image Manipulation Program > Vintage art digital files - fromoldbooks.org >

⚡Generated with XQuery