2

I have an XML document containing lists of people structured like this:

<listPerson>
 <person xml:id="John_de_Foo">
  <persName>
     <forename>John</forename>
     <nameLink>de</nameLink>
     <surname>Foo</surname>
     <role>snake charmer</role>
  </persName>
 </person>
 <person xml:id="John_de_Foo_jr">
  <persName>
     <forename>John</forename>
     <nameLink>de</nameLink>
     <surname>Foo</surname>
     <genname>junior</genname>
  </persName>
 </person>
 <person xml:id="M_de_Foo">
  <persName>
     <forename>M</forename>
     <nameLink>de</nameLink>
     <surname>Foo</surname>
  </persName>
 </person>
 [...]
</listPerson>

I am extracting only certain fields and concatenating them with tokenize() to create a new element <fullname> using XSL 3.0 (where $doc = current document):

<xsl:template match="listPerson/person"> 
 <fullname>
   <xsl:value-of select="$p/persName/name, $p/persName/forename, $p/persName/nameLink, $p/persName/surname, $p/persName/addName, $p/persName/genName" separator=" "/>
 </fullname>
<xsl:template/>

Outputs:

<fullname>John de Foo</fullname>
<fullname>John de Foo junior</fullname>
<fullname>M de Foo</fullname>

But, I'd like to treat the element <forename> with a specific test. If the forename/@text is a single initial, add . . The new result would output:

<fullname>John de Foo</fullname>
<fullname>John de Foo junior</fullname>
<fullname>M. de Foo</fullname>

Also, the <forename> element may not exist, in which case it should bypass the test.

I can do this by changing the tokenize() to a series of <xsl:if> statements, but I'd rather try to solve it within XPATH if possible.

Thanks in advance

jbrehr
  • 775
  • 6
  • 19
  • Your question is rather confusing, I see no `ana` attribute in your sample, you haven't shown us the declaration of the key you are trying to use, your XSLT outputs no `fullname` elements at all in your shown code snippet, yet you show output with e.g. `John de Foo junior`. If the question is only about outputting `pers/forename` with a full stop appended in case of a single letter use `pers/fullname!(if (string-length() eq 1) then . || '.' else .)` should suffice in XPath 3. – Martin Honnen Jun 15 '18 at 13:02
  • Sorry, I tried to keep the post to the problem code. Code now updated. Everything else in this code works fine. I am concatenating various elements to create a new element called `fullname`. In this concatenation I want to treat the `forename` field to add a `.` only in the case where it is one character in length (ie. an initial). – jbrehr Jun 15 '18 at 13:14
  • I simplified the code to reflect only the problem. It was a recursive code looking for info from other documents. This now just concerned with a simple transformation. – jbrehr Jun 15 '18 at 13:39

2 Answers2

5

In XPath 2.0 there is an if-then-else expression which you can use directly in the xsl:value-of like this:

<xsl:value-of select="$p/persName/name, if (string-length($p/persName/forename) = 1) then concat($p/persName/forename,'.') else $p/persName/forename , $p/persName/nameLink, $p/persName/surname, $p/persName/addName, $p/persName/genName" separator=" " />

An alternative is using the if-then-else in the concat(...) function:

<xsl:value-of select="$p/persName/name, concat($p/persName/forename, if (string-length($p/persName/forename) = 1) then '.' else ''), $p/persName/nameLink, $p/persName/surname, $p/persName/addName, $p/persName/genName" separator=" " />
zx485
  • 28,498
  • 28
  • 50
  • 59
  • I've tried both solutions, and neither has an effect, ie neither produces the `.` Is the implementation of `if` in XPATH dependent on the processor? I am using Saxon 9 HE. – jbrehr Jun 15 '18 at 13:24
  • That's strange. I tested it with Saxon 9 HE, too. – zx485 Jun 15 '18 at 13:29
3

If you want the forename to be suffixed with a full stop if it has a string length on one you can in XPath 3.1 use forename!(if (string-length() eq 1) then . || '.' else .) e.g. in a full template:

  <xsl:template match="persName">
      <fullname>
          <xsl:value-of select="forename!(if (string-length() eq 1) then . || '.' else .), nameLink, surname, genname" separator=" "/>
      </fullname>
  </xsl:template>

https://xsltfiddle.liberty-development.net/bdxtq5

Will of course work the same with a different expression e.g. $p/persName/forename!(if (string-length() eq 1) then . || '.' else .)

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110