3

I am very new with XSLT and using stylesheets, so maybe I am missing something obvious but I am calling the format-dateTime function in my xsl like below

                                <xsl:value-of
                                    select="format-dateTime(header/requestDetail/startDate,'[D01]/[M01]/[Y] [H01]:[m01]:[s01]','en',(),())" />

but this returns with the following error message

SystemId Unknown; Line #215; Column #118; Could not find function: format-dateTime
SystemId Unknown; Line #215; Column #118; A location path was expected, but the following token was encountered:  )
SystemId Unknown; Line #215; Column #118; A location path was expected, but the following token was encountered:  )
SystemId Unknown; Line #215; Column #118; function token not found.
(Location of error unknown)java.lang.NullPointerException
Exception in thread "main" java.lang.NullPointerException

Could someone tell me as to why this is happening, because as far as I have read format-dateTime is built in function for XSLT 2.0. I also have the latest version of SAXON in my maven dependencies.

Here is the complete xsl file

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
    <xsl:output encoding="UTF-8" indent="yes" method="xml"
        standalone="no" omit-xml-declaration="no" />
    <xsl:template match="users-data">
        <fo:root language="EN">
            <fo:layout-master-set>
                <fo:simple-page-master master-name="A4-portrail"
                    page-height="297mm" page-width="210mm" margin-top="5mm"
                    margin-bottom="5mm" margin-left="5mm" margin-right="5mm">
                    <fo:region-body margin-top="25mm" margin-bottom="20mm" />
                    <fo:region-before region-name="xsl-region-before"
                        extent="25mm" display-align="before" precedence="true" />
                </fo:simple-page-master>
            </fo:layout-master-set>
            <fo:page-sequence master-reference="A4-portrail">
                <fo:static-content flow-name="xsl-region-before">
                    <fo:table table-layout="fixed" width="100%" font-size="10pt"
                        border-color="black" border-width="0.4mm" border-style="solid">
                        <fo:table-column column-width="proportional-column-width(20)" />
                        <fo:table-column column-width="proportional-column-width(45)" />
                        <fo:table-column column-width="proportional-column-width(20)" />
                        <fo:table-body>
                            <fo:table-row>
                                <fo:table-cell text-align="left" display-align="center"
                                    padding-left="2mm">
                                    <fo:block>
                                        Bill Id:
                                        <xsl:value-of select="header-section/data-type/@id" />
                                        , Date:
                                        <xsl:value-of select="header-section/process-date" />
                                    </fo:block>
                                </fo:table-cell>
                                <fo:table-cell text-align="center" display-align="center">
                                    <fo:block font-size="150%">
                                        <fo:basic-link external-destination="http://www.example.com">XXX COMPANY
                                        </fo:basic-link>
                                    </fo:block>
                                    <fo:block space-before="3mm" />
                                </fo:table-cell>
                                <fo:table-cell text-align="right" display-align="center"
                                    padding-right="2mm">
                                    <fo:block>
                                        <xsl:value-of select="data-type" />
                                    </fo:block>
                                    <fo:block display-align="before" space-before="6mm">
                                        Page
                                        <fo:page-number />
                                        of
                                        <fo:page-number-citation ref-id="end-of-document" />
                                    </fo:block>
                                </fo:table-cell>
                            </fo:table-row>
                        </fo:table-body>
                    </fo:table>
                </fo:static-content>
                <fo:flow flow-name="xsl-region-body" border-collapse="collapse"
                    reference-orientation="0">
                    <fo:block>MONTHLY BILL REPORT</fo:block>
                    <fo:table table-layout="fixed" width="100%" font-size="10pt"
                        border-color="black" border-width="0.35mm" border-style="solid"
                        text-align="center" display-align="center" space-after="5mm">
                        <fo:table-column column-width="proportional-column-width(20)" />
                        <fo:table-column column-width="proportional-column-width(30)" />
                        <fo:table-column column-width="proportional-column-width(25)" />
                        <fo:table-column column-width="proportional-column-width(50)" />
                        <fo:table-body font-size="95%">
                            <fo:table-row height="8mm">
                                <fo:table-cell>
                                    <fo:block>Full Name</fo:block>
                                </fo:table-cell>
                                <fo:table-cell>
                                    <fo:block>Postal Code</fo:block>
                                </fo:table-cell>
                                <fo:table-cell>
                                    <fo:block>National ID</fo:block>
                                </fo:table-cell>
                                <fo:table-cell>
                                    <fo:block>Payment</fo:block>
                                </fo:table-cell>
                            </fo:table-row>
                            <xsl:for-each select="user-bill-data">
                                <fo:table-row>
                                    <fo:table-cell>
                                        <fo:block>
                                            <xsl:value-of select="full-name" />
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell>
                                        <fo:block>
                                            <xsl:value-of select="postal-code" />
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell>
                                        <fo:block>
                                            <xsl:value-of select="national-id" />
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell>
                                        <fo:block>
                                            <xsl:value-of
                                                select="format-dateTime(header/requestDetail/startDate,'[D01]/[M01]/[Y] [H01]:[m01]:[s01]','en',(),())" />
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell border="1pt solid black"
                                        padding-left="2pt" text-align="left" padding-right="2pt">
                                        <fo:block font="8pt Helvetica">
                                            <xsl:value-of
                                                select="format-dateTime(header/requestDetail/endDate,'[D01]/[M01]/[Y] [H01]:[m01]:[s01]','en',(),())" />
                                        </fo:block>
                                    </fo:table-cell>
                                    <xsl:value-of select="price" />
                                </fo:block>
                                </fo:table-cell>
                                </fo:table-row>
                            </xsl:for-each>
                        </fo:table-body>
                    </fo:table>
                    <fo:block id="end-of-document">
                        <fo:instream-foreign-object>
                            <svg width="200mm" height="150mm" version="1.1"
                                xmlns="http://www.w3.org/2000/svg">
                                <path
                                    d="M153 334
C153 334 151 334 151 334
C151 339 153 344 156 344
C164 344 171 339 171 334
C171 322 164 314 156 314
C142 314 131 322 131 334
C131 350 142 364 156 364
C175 364 191 350 191 334
C191 311 175 294 156 294
C131 294 111 311 111 334
C111 361 131 384 156 384
C186 384 211 361 211 334
C211 300 186 274 156 274"
                                    style="fill:yellow;stroke:red;stroke-width:2" />
                            </svg>
                        </fo:instream-foreign-object>
                    </fo:block>
                </fo:flow>
            </fo:page-sequence>
        </fo:root>
    </xsl:template>
</xsl:stylesheet>

Here is the Java class, which uses FOP to transform into PDF

public final class PDFConverter {

private PDFConverter() {

}

private static final String RESOURCES_DIR;
private static final String OUTPUT_DIR;

static {
    RESOURCES_DIR = "src//main//resources//";
    OUTPUT_DIR = "src//main//resources//output//";
}

public static void main(final String[] args) {
    try {
        convertToPDF();
    } catch (FOPException | IOException | TransformerException e) {
        e.printStackTrace();
    }
}

public static void convertToPDF() throws IOException, FOPException, TransformerException {
    // the XSL FO file
    File xsltFile = new File(RESOURCES_DIR + "//template.xsl");
    // the XML file which provides the input
    StreamSource xmlSource = new StreamSource(new File(RESOURCES_DIR + "//data.xml"));
    // create an instance of fop factory
    FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
    // a user agent is needed for transformation
    FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
    // Setup output
    OutputStream out;
    out = new java.io.FileOutputStream(OUTPUT_DIR + "//output.pdf");

    try {
        // Construct fop with desired output format
        Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);

        // Setup XSLT
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(new StreamSource(xsltFile));

        // Resulting SAX events (the generated FO) must be piped through to
        // FOP
        Result res = new SAXResult(fop.getDefaultHandler());

        // Start XSLT transformation and FOP processing
        // That's where the XML is first transformed to XSL-FO and then
        // PDF is created
        transformer.transform(xmlSource, res);
    } finally {
        out.close();
    }
}

}

user6248190
  • 1,233
  • 2
  • 16
  • 31
  • Can you tell us which Saxon version and edition you have on the class path? Your stylesheet says `version="1.0"` and Saxon 9.8 HE for instance would not run that, although that should result in a different error message. How do you run the XSLT, are you sure it is Saxon used as the XSLT processor by FOP? – Martin Honnen Oct 13 '17 at 09:01
  • @MartinHonnen I am using Sason-HE-9.8.0-5.in my class path. In order to run the XSLT I have a java class which uses FOP which transforms into PDF. – user6248190 Oct 13 '17 at 09:14
  • 1
    As I said, if it were Saxon 9.8 HE running that `version="1.0"` stylesheet it would raise an error that it does not support backwards compatibility, so based on that I think your Java class does not use Saxon but rather Xalan to transform the stylesheet and that way `format-dateTime` is not found and `()` raises that syntax error. So add a tag for Java or FOP and show the Java code so that someone familiar with that can suggest how to ensure that Saxon is used and not Xalan. You will also need to change the XSLT version to `3.0`. – Martin Honnen Oct 13 '17 at 11:03
  • Note also that you can check which TransformerFactory has been loaded by showing the class name that comes back from the newInstance() method; you can also use the system property jaxp.debug to display diagnostic information. But unless you really want your application to run with whatever XSLT processor it finds on the class path, it's better not to rely on JAXP to find the right one, and instead load it explicitly. – Michael Kay Oct 13 '17 at 12:12

1 Answers1

4

See http://saxonica.com/html/documentation/using-xsl/embedding/jaxp-transformation.html on how to make sure that Saxon is used as the Transformer, it says

If you want to be sure that Saxon (and not some other XSLT engine) is loaded to perform your transformations, you need to know the class name of Saxon's implementation of the JAXP TransformerFactory class. This depends on which Saxon edition you are using:

Any: net.sf.saxon.TransformerFactoryImpl. This will create a Saxon configuration based on the software and license file that are available at run-time on the classpath.

Based on that you can change TransformerFactory factory = TransformerFactory.newInstance(); to TransformerFactory factory = new net.sf.saxon.TransformerFactoryImpl(); and then your code should use Saxon.

Also don't forget to set version="3.0" in your XSLT code if you use Saxon 9.8 HE as it does not support running in backwards compatibility code for version="1.0" stylesheets.

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