-1

I create an XML using a my software (i. e. I have control over its structure). I want to use this XML to generate varying results by using different XSLT translation files. How can I generate varying texts in the result, depending on the XSLT in use?

More concrete:

My resulting documents shall contain either a "dots" character, i. e. or three dots, i. e. ..., depending on the XSLT used. With one XSLT my XML shall result in the first, with another XSLT the same XML (!) shall result in the second. I need to write the XSLTs so that they translate the XML once to the one and once to the other.

My first approach was to replace a substring of the input. Various other questions exist for this topic but so far none fit my situation:

  • using in the XML and then using replace() in XSLT — but I'm using XSLT 1.0 which doesn't contain this nice function.
  • using in the XML and then using translate() in XSLT — but this can only translate one character to one character, while I need to have ... as one of the outputs (i. e. more than one character).
  • using entities (e. g. &dots;) in the XML and have the XSLT expand these to varying strings — the only approach for doing this I found treated the entities exactly like any other string, so no solution found for this either. I still hope I just didn't find a nice and easy solution using this approach.

There were laborious solutions for implementing a replace()-like thing in XSLT 1.0. Before using these I'd rather create different XMLs :-/

I'm surprised that my problem seems so hard to solve and have the feeling I've overlooked something simple.

Alfe
  • 56,346
  • 20
  • 107
  • 159
  • So does the string in the result (i.e. either `…` or `...`) depend on anything in the input XML at all or solely on the used XSLT? If you need one XSLT to output one string and the other to output a second then if you use it literally in the stylesheet code I don't see why you would need to use translate or replace. – Martin Honnen Oct 10 '17 at 21:24
  • 1
    What's remarkable about different transforms transforming the same input differently? – John Bollinger Oct 10 '17 at 21:47
  • Do you intend to use your transforms in a context where you can rely on providing your extension functions? – John Bollinger Oct 10 '17 at 21:49
  • It does not depend on the XML, no. The XML states "here be an ellipsis" (either using `…` or `&dots;` or whatever), and the XSLT is supposed to interpret that into either `…` or `...` in the output. What you write about style sheets intrigues me. Do you see a way of doing this with another means? Maybe my question should have been "How would you solve this task? If not by XSLT than by what means?" XSLT looked the natural way of approaching it because I'm using XSL-FO for typesetting documents here, and the PDF renderer (Apache FOP) allows naming an XSLT for transforming the XML into FO. – Alfe Oct 10 '17 at 21:50
  • @JohnBollinger I'm sorry, I didn't understand what you are asking in your second comment. Concerning the first: I don't find anything remarkable about it. What I find remarkable is that it seems to be a problem translating text specifically in an XSLT. – Alfe Oct 10 '17 at 21:54
  • @Alfe, XPath and therefore XSLT provide an extension mechanism by which you could conceivably supply your own `replace()` function. That route is only viable, however, if you have sufficient control over the context in which your transforms are used to in fact provide for extensions to be accessible to them. – John Bollinger Oct 10 '17 at 22:00
  • @JohnBollinger Now I understand that you are concerned about who is going to use this and fiddle around with the input etc. I'm in complete charge, I work alone here. I provide the XML input (structure and contents), the XSLT transformation, all style sheets, etc. I am currently using FOP for rendering the XML into PDFs but I could even change that if need be. – Alfe Oct 10 '17 at 22:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/156394/discussion-between-alfe-and-john-bollinger). – Alfe Oct 10 '17 at 22:12
  • "I'm surprised the problem seems so hard". Of course it's hard. XSLT 1.0 was developed for a specific set of use cases. It was quickly found that people wanted to use it for other things, and the language was developed to make those things easy. You're using old technology: why? – Michael Kay Oct 10 '17 at 22:14
  • @MichaelKay I'd love to use sth more modern. FOP seems to support only XSLT 1.0, that's all :-/ – Alfe Oct 10 '17 at 22:16
  • FOP takes XML as input, that XML can be produced by any XSLT processor (or indeed by anything else). FOP has no dependency on any version of XSLT. See for example https://stackoverflow.com/questions/19379070/how-to-change-apache-fop-xalan-xslt-processor – Michael Kay Oct 10 '17 at 22:21
  • @MichaelKay I produced different versions of XML-FO directly before, and that worked but produced always several versions of this. I'm currently trying to unify all into one XML and then just provide the correct XSLT to FOP directly in the call. FOP should translate the unified XML into the proper FO then and render it in one step. – Alfe Oct 10 '17 at 22:26

3 Answers3

1

XSLT, especially version 1.0, has a coarser focus and is more minimalist than you would like. It is tooled for handling nodes, their children, and their values, but its built-in facilities for string manipulation are limited. If you are not prepared to engage extension functions, then that means you may need to write your own XSLT for some things.

For example, suppose you produce XML containing one-character elipses. One of your transforms can simply pass these through as-is. Your other might use a mechanism such as this to transform them into three dots:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="text()">
    <xsl:call-template name="translate-elipsis">
      <xsl:with-param name="text" select="."/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="translate-elipsis">
    <xsl:param name="text"/>
    <xsl:choose>
      <xsl:when test="contains($text,'…')">
        <xsl:value-of select="substring-before($text,'…')"/>
        <xsl:text>...</xsl:text>
        <xsl:call-template name="translate-elipsis" select="substring-after($text,'…')"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

That transforms the contents of all text nodes for which there is not a more specific match; you could engage it to transform attribute values as well if you needed to do. It's certainly more verbose than a single function call, but not outrageous, I think. You can use a similar pattern to write a generalized template for string replacement.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thanks, that's clear enough for my taste and it works, but weirdly it removed the space between the `…` and the next word. I could add it in the `` tag but of course there's not always a space after the ellipsis. Any idea why it's swallowing up the space and how to fix it? – Alfe Oct 10 '17 at 22:31
  • Wait, I found that not just a space was missing. Actually everything from the ellipsis up to the next opening tag was missing. – Alfe Oct 10 '17 at 22:56
1

As you say you are in command of the XML it seems one way simply would be to mark up that particular data in an element of its own e.g.

<p>This is a text with an <ellipsis/>.</p>

and then in the stylesheet where you want to have the three dots you would simply have a template

<xsl:template match="ellipsis">
  <xsl:text>...</xsl:text>
</xsl:template>

where as the other stylesheet would output the character

<xsl:template match="ellipsis">
  <xsl:text>…</xsl:text>
</xsl:template>

Other than that I agree with the suggestions and comments made, these days with XSLT 2.0 or even 3.0 supported on the Java platform noone is forced to use FOP with the built-in XSLT 1.0 processor in the JRE.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Of course! That's a simple solution of the kind I was looking for :-) Will accept after testing. – Alfe Oct 11 '17 at 08:48
0

I solved my issue with FOP using some advice from https://stackoverflow.com/a/12364925/1281485 : After adding some name spaces I had access to a replaceAll() which works as expected it seems:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo="http://www.w3.org/1999/XSL/Format"
                xmlns:xalan="http://xml.apache.org/xalan"
                xmlns:str="xalan://java.lang.String">

and later:

  <xsl:template match="text()">
    <xsl:value-of select="str:replaceAll(str:new(.), '…', '...')"/>
  </xsl:template>

I'll maybe update my answer if I find any issues using this in the future. Up to now it seems to solve my task.

Alfe
  • 56,346
  • 20
  • 107
  • 159