Xquery to get a string between delimiters

Viewed 49

I have (TEI)XML with bits like <ref target="#a1"/> but also <ref target="#a1 #b2 #c3"/> I need to write the xquery code to transform each #-target to a link; this is no problem if I have just one target="#a1", then I use substring-after($node/@target, '#') and look for xml-ids that are the same as the substring, but then of course I get problems if the substring is longer than needed, no match is possible. Is there a way to select the substring between # and space or # and "? (I am very new to xquery so sorry if I am asking something very obvious, but I could not find an economic solution)

1 Answers

You could tokenize() the attribute value by an optional space \s and # with this regex \s?# and then filter out an empty item with a predicate using normalize-space() or testing the string length string-length(.) gt 0:

let $ref :=  <ref target="#a1 #b2 #c3"/>
let $targets := tokenize($ref/@target, '\s?#')[normalize-space()]
return  
  $targets

or you could tokenize by space \s or space and # \s#, then translate() any remaining # into nothing:

let $ref :=  <ref target="#a1 #b2 #c3"/>
let $targets := tokenize($ref/@target, '\s#') ! translate(., '#', '')
return  
  $targets

Another way to read the space separated ref/@target attribute values as a sequence is to read them as xs:NMTOKENS (which are space separated values), and then you just need to worry about removing the # from each value:

let $ref :=  <ref target="#a1 #b2 #c3"/>
let $target-tokens as xs:NMTOKENS := $ref/@target
let $targets :=  $target-tokens ! translate(., '#', '')
return
  $targets 
Related