I am using the Saxon XsltTransformer to transform the XML of electronic invoices ("XRechnungen", the German adaption).
The invoices are XML files with "un/cefact CII" or "UBL" syntax and both need to be translated in the same XR syntax.
For UBL I use the files "ubl_invoice_xr.xsl" and "ubl_creditnore_xr.xsl" and this works without any problems.
For CII I use the "cii_xr.xsl" from
https://github.com/itplr-kosit/xrechnung-visualization/tree/master/src/xsl
but the result omits the allowances and charges (both on invoice_line-level and document-level).
At https://github.com/itplr-kosit/xrechnung-testsuite/tree/master/src/test/business-cases/standard
I found a lot of test invoices ("uncefact" suffix) bot none of them is transformed correctly.
Looking at the "cii-xr.xsl" you find:
<xsl:template mode="BG-20"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge[ram:ChargeIndicator='false']">
<xsl:variable name="bg-contents" as="item()*"><!--Der Pfad /rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge[ram:ChargeIndicator='false'] der Instanz in konkreter Syntax wird auf 7 Objekte der EN 16931 abgebildet. -->
<xsl:apply-templates mode="BT-92" select="./ram:ActualAmount"/>
<xsl:apply-templates mode="BT-93" select="./ram:BasisAmount"/>
<xsl:apply-templates mode="BT-94" select="./ram:CalculationPercent"/>
<xsl:apply-templates mode="BT-95" select="./ram:CategoryTradeTax/ram:CategoryCode"/>
<xsl:apply-templates mode="BT-96" select="./ram:CategoryTradeTax/ram:RateApplicablePercent"/>
<xsl:apply-templates mode="BT-97" select="./ram:Reason"/>
<xsl:apply-templates mode="BT-98" select="./ram:ReasonCode"/>
</xsl:variable>
<xsl:if test="$bg-contents">
<xr:DOCUMENT_LEVEL_ALLOWANCES>
<xsl:attribute name="xr:id" select="'BG-20'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:sequence select="$bg-contents"/>
</xr:DOCUMENT_LEVEL_ALLOWANCES>
</xsl:if>
</xsl:template>
<xsl:template mode="BT-92"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:ActualAmount">
<xr:Document_level_allowance_amount>
<xsl:attribute name="xr:id" select="'BT-92'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:call-template name="amount"/>
</xr:Document_level_allowance_amount>
</xsl:template>
<xsl:template mode="BT-93"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:BasisAmount">
<xr:Document_level_allowance_base_amount>
<xsl:attribute name="xr:id" select="'BT-93'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:call-template name="amount"/>
</xr:Document_level_allowance_base_amount>
</xsl:template>
<xsl:template mode="BT-94"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:CalculationPercent">
<xr:Document_level_allowance_percentage>
<xsl:attribute name="xr:id" select="'BT-94'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:call-template name="percentage"/>
</xr:Document_level_allowance_percentage>
</xsl:template>
<xsl:template mode="BT-95"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:CategoryTradeTax/ram:CategoryCode">
<xr:Document_level_allowance_VAT_category_code>
<xsl:attribute name="xr:id" select="'BT-95'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:call-template name="code"/>
</xr:Document_level_allowance_VAT_category_code>
</xsl:template>
<xsl:template mode="BT-96"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:CategoryTradeTax/ram:RateApplicablePercent">
<xr:Document_level_allowance_VAT_rate>
<xsl:attribute name="xr:id" select="'BT-96'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:call-template name="percentage"/>
</xr:Document_level_allowance_VAT_rate>
</xsl:template>
<xsl:template mode="BT-97"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:Reason">
<xr:Document_level_allowance_reason>
<xsl:attribute name="xr:id" select="'BT-97'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:call-template name="text"/>
</xr:Document_level_allowance_reason>
</xsl:template>
<xsl:template mode="BT-98"
match="/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:ReasonCode">
<xr:Document_level_allowance_reason_code>
<xsl:attribute name="xr:id" select="'BT-98'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:call-template name="code"/>
</xr:Document_level_allowance_reason_code>
</xsl:template>
So the output file should contain "<xr:DOCUMENT_LEVEL_ALLOWANCES>" tags with (for example)
"</xr:Document_level_allowance_amount>" children, but it does not.
No matter if the input file has allowances/charges in the "invoice_lines" or at "document_level".
Since I use vb.NET I use the .NET wrapper of saxon-HE:
Public Shared Function Cii2UnifiedXML(ByVal vsXmlFilePath As String) As Result
Dim cproc As New Saxon.Api.Processor
Dim cComp As Saxon.Api.XsltCompiler = cproc.NewXsltCompiler
Dim exe As Saxon.Api.XsltExecutable
Dim inputStream As New StreamReader(vsXmlFilePath)
#Disable Warning BC40000 ' I know, XmlDataDocument is deprecated
Dim xmldoc As New XmlDataDocument()
#Enable Warning BC40000
Dim cResult As New Result
Dim ctrans As Saxon.Api.XsltTransformer
Dim seri As Saxon.Api.Serializer
Dim sPath As String = String.Empty
Dim sFilename As String = String.Empty
Dim outStream As FileStream = Nothing
Try
exe = cComp.Compile(New MemoryStream(Encoding.UTF8.GetBytes(My.Resources.cii_xr)))
ctrans = exe.Load
seri = cproc.NewSerializer
sPath = Path.GetDirectoryName(vsXmlFilePath)
sFilename = Path.Combine(sPath, $"{Path.GetFileNameWithoutExtension(vsXmlFilePath)}_Unified.xml")
outStream = New FileStream(sFilename, FileMode.Create, FileAccess.Write)
ctrans.SetInputStream(inputStream.BaseStream, New Uri(sPath))
seri.SetOutputStream(outStream)
ctrans.Run(seri)
cResult.Success = True
cResult.Obj = sFilename
Catch ex As Exception
cResult.Exception = ex
cResult.Success = False
cResult.ErrorMsg = ex.Message
Finally
xmldoc = Nothing
If outStream IsNot Nothing Then outStream.Close()
If inputStream IsNot Nothing Then inputStream.Close()
End Try
Return cResult
End Function
I don't know if I don't understand how the XsltCompiler works (and therefore I am too stupid) or if it's a bug, since the translation of UBL invoices works great.
The output file of UBL translations contains the elements "DOCUMENT_LEVEL_ALLOWANCES", "DOCUMENT_LEVEL_CHARGES", "INVOICE_LINE_ALLOWANCES" and "INVOICE_LINE_CHARGES".
All files can be found at https://github.com/itplr-kosit
Thanks a lot
It's hard to make much headway on debugging this for you because I don't understand the XML vocabulary or the logic of the stylesheet. However, I tried it on one sample document from the test set, and the template rule you show us with
<xsl:template mode="BG-20" match="/rsm:CrossIndustryInvoice
/rsm:SupplyChainTradeTransaction
/ram:ApplicableHeaderTradeSettlement
/ram:SpecifiedTradeAllowanceCharge[ram:ChargeIndicator='false']">
doesn't get executed because the ApplicableHeaderTradeSettlement element in the source document doesn't have a child called SpecifiedTradeAllowanceCharge.
Of course I've no idea what this means or how you fix it, but there's no evidence that the stylesheet isn't doing what it was designed to do on the one source document that I tested.
The match is /rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge[ram:ChargeIndicator='false'] looking for the value false in the predicate while your input sample seems to have the value true. So that could be one reason why the templates you have cited are not applied.
Another reason could be the wrong predicate or the lack of <xsl:strip-space/> although the online XSLT seems to use the right predicate [ram:ChargeIndicator/udt:Indicator='false'], i.e. with the additional child.
the problem was found, you won't believe what caused it...
we tried several XR-visualizer that showed the charges and allowances correctly.
We tried several translation tools (web + desktop) but none of them created the output file correctly (with allowances and charges).
Most validator tools for "XPath" showed the invoices as "correct" but only ONE told us, that there is a line break between the "indicator" element and it's parent element "ChargeIndicator".
Of course... there are hundreds of line breaks everywhere?
This block in the cii invoice file does NOT work:
<ram:ChargeIndicator>
<udt:Indicator>true</udt:Indicator>
</ram:ChargeIndicator>
but this works:
<ram:ChargeIndicator><udt:Indicator>true</udt:Indicator></ram:ChargeIndicator>
I tried to contact the kosit several times but they have no telephone hotline for development issues and I did not get a reply to the mails I wrote 8, 5 and 2 weeks ago.
That's funny, because the version XRechnung 2.0 will be mandatory to use by 1st jan. 2021.
Thanks for all your ideas guys
Say you have an XSLT which is currently in use for a myriad of cases. Given that it does not currently have any <xsl:template match="/">; would adding the following template interfere with anything?
<xsl:template match="/">
<xsl:choose>
<xsl:when test="some condition which is only true for new cases"></xsl:when>
<xsl:otherwise>
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Reason I'm asking is that we have a huge XSLT file which is currently in use and should not be broken, but we need to add some new transformations to it. Making some decisions on the root would make things a lot easier and cleaner.
I was just told that if there is no template matching /, then a default template equivalent to <xsl:apply-templates /> will be applied. So, just wanting to check if my understand is correct, and that this then (as long as the test is only true for new cases) would not break any existing transformations.
That's correct, you can handle your conditions within the root template "/" and just apply-templates for the "normal" treatement.
But check existing root templates matching "/" - maybe it is already doing more than just apply the next templates.
I am mapping to an HL7 A31 message using the BizTalk mapper. The map has several inline XSLT scripting functoids.
When the XML is put through the HL7 send pipeline, it generates an error:
The element 'ROL_11_OfficeHomeAddress' has an invalid structure
If I look at the suspended message, I can see why this has happened. The ROL_11 element is empty, and looks like this:
<ROL_11_OfficeHomeAddress>
</ROL_11_OfficeHomeAddress>
Between the opening and closing tags, there is a line break and several spaces/tabs due to indenting. This is exactly as generated by the XSLT and I believe it is the line break that is causing the error.
I could wrap the XSLT in an <xsl:if> statement to check for a value before writing the XML. However this problem is occurring in many places and it seems overkill to wrap every single element like this.
What I really want is for BizTalk to automatically convert the element to an empty one, like this:
<ROL_11_OfficeHomeAddress />
I believe this would solve the problem. Is there any way I can tell it to do that?
Things I have already tried:
Using <xsl:strip-space> but that raised its own error. I think this is because BizTalk wraps the inline XSLT in its own code and thus strip-space was specified in the wrong place.
Changing the map's grid properties to set Indent to No in the hope the whitespace would be removed. This had no effect on the XML seen in the suspended message.
Adding the registry key for legacy whitespace handling as per this guidance. Again, this appeared to have no effect at all.
If you convert your entire map into XSLT, the below will strip out newlines and whitespace and leave you with an empty tag if there isn't anything but whitespace:
<xsl:element name="ROL_11_OfficeHomeAddress">
<xsl:if test="normalize-space(ROL_11_OfficeHomeAddress)">
<xsl:value-of select="normalize-space(ROL_11_OfficeHomeAddress)" />
</xsl:if>
</xsl:element>
Edit:
Biztalk usually generates XSLT like the following in a typical 1:1 nillable element mapping
<xsl:variable name="var:v2" select="string(ns0:ROL_11_OfficeHomeAddress/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v2)='true'">
<ns0:ROL_11_OfficeHomeAddress>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</ns0:ROL_11_OfficeHomeAddress>
</xsl:if>
<xsl:if test="string($var:v2)='false'">
<ns0:ROL_11_OfficeHomeAddress>
<xsl:value-of select="ROL_11_OfficeHomeAddress/text()" />
</ns0:ROL_11_OfficeHomeAddress>
</xsl:if>
So if you did use <xsl:strip-space> it would mean that the element would map to <ROL_11_OfficeHomeAddress></ROL_11_OfficeHomeAddress> if whitespace only, unless you went through the map changing it back to <xsl:element>.
What you could try is to use a call template like the below (nodeXfrm is a node)
<xsl:template name="StripElement">
<xsl:param name="nodeXfrm"></xsl:param>
<xsl:variable name="nodeName">
<xsl:value-of select="local-name($nodeXfrm)"></xsl:value-of>
</xsl:variable>
<xsl:element name="{$nodeName}">
<xsl:if test="normalize-space($nodeXfrm)!=''">
<xsl:value-of select="$nodeXfrm/text()"/>
</xsl:if>
</xsl:element>
</xsl:template>
And then within your map you can call the template for each element you need stripped in this way
<xsl:call-template name="StripElement">
<xsl:with-param name="nodeXfrm" select="ROL_11_OfficeHomeAddress"></xsl:with-param>
</xsl:call-template>
An XSLT guru might be able to do this more elegantly
I too was recently having this problem, but in BizTalk 2013. We moved everything to custom XSLT files for mapping our HL7v2. Upon upgrading to 2013, suddenly the <xsl:strip-space> that previously worked, no longer worked.
This is because BizTalk 2013 now uses the XslCompiledTransform class rather than the now obsoleted XslTransform class and it doesn't allow the <xsl:strip-space>. So I too was faced with no global way to strip the whitespace.
However, after much searching and head scratching, I found an obscure blog post with something that worked for my solution:
http://geekswithblogs.net/peterbrouwer/archive/2012/08/17/biztalk-2010ndashlegacy-whitespace-behaviour.aspx
An option in the the Host's settings, for using legacy whitespace did it for us (so far at least).
I want to create a Element in xml <a:ln w="12700"> using xslt 1.0.
and this is what i did in xslt
<xsl:variable name="width-value">12700</xsl:variable>
<xsl:element name="a:ln">
<xsl:attribute name="w">
<xsl:value-of select="$width-value"/>
</xsl:attribute>
</xsl:element>
its throwing error,
You cannot call an attribute 'w''
FATAL ERROR: 'Could not compile stylesheet' javax.xml.transform.TransformerConfigurationException: Could not compile stylesheet
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTemplates(TransformerFactoryImpl.java:825)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:614)
at com.vignesh.main.convert(main.java:288)
at com.vignesh.main.main(main.java:70)
The only thing that I see is that you're missing the namespace declaration for a: If I have to make this snippet working, I must add something like this
<xsl:element name="a:ln" namespace="http://www.example.com/a/">
Perhaps you have the a namespace declaration elsewhere. I suspect the problem is due to some other part of your XSL, because this snippet looks OK to me.
I've found a lot of examples that reference Java and C for this, but how do I, or can I, check for the existence of an external file with XSL.
First, I realize that this is only a snippet, but it's part of a huge stylesheet, so I'm hoping it's enough to show my issue.
<!-- Use this template for Received SMSs -->
<xsl:template name="ReceivedSMS">
<!-- Set/Declare "SMSname" variable (local, evaluates per instance) -->
<xsl:variable name="SMSname">
<xsl:value-of select=" following-sibling::Name"/>
</xsl:variable>
<fo:table font-family="Arial Unicode MS" font-size="8pt" text-align="start">
<fo:table-column column-width=".75in"/>
<fo:table-column column-width="6.75in"/>
<fo:table-body>
<fo:table-row>
<!-- Cell contains "speakers" icon -->
<fo:table-cell display-align="after">
<fo:block text-align="start">
<fo:external-graphic src="../images/{$SMSname}.jpg" content-height="0.6in"/>
What I'd like to do, is put in an "if" statement, surronding the {$SMSname}.jpg line. That is:
<fo:block text-align="start">
<xsl:if test="exists( the external file {$SMSname}.jpg)">
<fo:external-graphic src="../images/{$SMSname}.jpg" content-height="0.6in"/>
</xsl:if>
<xsl:if test="not(exists( the external file {$SMSname}.jpg))">
<fo:external-graphic src="../images/unknown.jpg" content-height="0.6in"/>
</xsl:if>
</fo:block>
Because of "grouping", etc., I'm using XSLT 2.0. I hope that this is something that can be done. I hope even more that it's something simple.
As always, thanks in advance for any help.
LO
For anyone else who might stumble upon this old question, by piecing together information from various sources on the internet, I came up with this XSLT2 function that uses a Java extension for checking whether a file exists:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:java="http://www.java.com/"
exclude-result-prefixes="java xs">
<xsl:function name="java:file-exists" xmlns:file="java.io.File" as="xs:boolean">
<xsl:param name="file" as="xs:string"/>
<xsl:param name="base-uri" as="xs:string"/>
<xsl:variable name="absolute-uri" select="resolve-uri($file, $base-uri)" as="xs:anyURI"/>
<xsl:sequence select="file:exists(file:new($absolute-uri))"/>
</xsl:function>
</xsl:stylesheet>
You can then use the function like this:
<xsl:if test="java:file-exists($filename, base-uri())">
<!-- ... -->
</xsl:if>
This works at least with Saxon 9.1.0.5J — haven't tested it with any other XSLT processors.
Note: If the file for whose existence you're checking is an XML file and you're using XSLT2, you can also use the built-in doc-available() function:
<xsl:if test="doc-available('hello_world.xml')">
<!-- ... -->
</xsl:if>
No, this cannot be done using XSLT 2.0/XPath 2.0.
The XSLT 2.0 function unparsed-text-available() is only suitable for locating text files and even if a text file with the specifies URI exists this function may return false(), because it also must read the contents of the file and check that it only contains allowed characters.
From the spec:
"The unparsed-text-available function determines whether a call on the unparsed-text function with identical arguments would return a string.
If the first argument is an empty sequence, the function returns false. If the second argument is an empty sequence, the function behaves as if the second argument were omitted.
In other cases, the function returns true if a call on unparsed-text with the same arguments would succeed, and false if a call on unparsed-text with the same arguments would fail with a non-recoverable dynamic error.
Note:
This requires that the unparsed-text-available function should actually attempt to read the resource identified by the URI, and check that it is correctly encoded and contains no characters that are invalid in XML
"
End of quotation.
In case someone else needs it and is using fop, I would like to share this answer since it worked for me like a charm.
I've found a solution:
<xsl:when test="fs:exists(fs:new('myfile.html'))" xmlns:fs="java.io.File">
<!-- do something here... -->
</xsl:when>
and it works independently of XSLT 1.0 or 2.0
EDIT: Use unparsed-text-available function. It is part of xslt 2.0, but not XQuery or standalone XPath.
I've left my previous answer here so you can follow the trail of uncertainty...
I don't believe there is a way of doing this in XSLT using the standard functions. You can do it using extension functions, as described here, for java.
There is the unparsed-text-available function, but I'm unsure if this is a standard function. There's an example of it's usage at Zvon. The unparsed-text-available is mentioned here as being part of xslt 2.0, and is supported in Saxon.
A proposed File Module EXPath specification would support file-system functions such as this (file:exists() in the spec) as standard XPath extension functions. There isn't yet an XSLT implementation for this, but its worth watching.
Back to pgfearo's notice.
A proposed File Module EXPath specification would support file-system
functions such as this (file:exists() in the spec) as standard XPath
extension functions. There isn't yet an XSLT implementation for this,
but its worth watching.
For those who need to check if an file exists or not.
file:exists($path as xs:string)
works fine now.
If you still wants to do it in XSLT here is the solution, I have done it for myself as explained below.
This will not working with regular java.io.File class in XSLT. So I have used java.nio.file.Files class.
JARS required - servelt.jar
w: is the namespace of the our own Java class where pathFromURI method is defined.
Code:
<xsl:variable name="fileURI" select="u:new($absoluteFilePath)" xmlns:u="java:java.net.URI"/>
<xsl:variable name="filePathFromURI" select="w:pathFromURI($fileURI)"/>
<xsl:variable name="fileNotExist" select="not(files:exists($filePathFromURI, /..)) or files:size($filePathFromURI) = 0"/>
public static java.nio.file.Path pathFromURI(java.net.URI uri) throws Exception {
return java.nio.file.Paths.get(uri);
}
It can't solely be done by standard XSLT, you have to use an extention function or some annoying workaround. There are two methods using extension functions: use of standard java/.NET for custom functions (works with some versions of Saxon, AltovaXML, and others), or use of processor specific extension functions, like saxon:file-last-modified()/saxon:last-modified(). You can find some sample code here, look for intern:file-exists().
If you can't use extension functions, you can either generate an XML file externaly which contains informations about your file system and pass it to your stylesheet, or you can wrap binary images within SVG and than use fn:doc-available().
If you need to check for the existence of an XML file, use an external entity and an inline doctype:
<!DOCTYPE foo [ <!ENTITY bar SYSTEM "baz.xml"> ]>
Then add the entity to the stylesheet:
&bar;
The processor will timeout if the file is not found.
If you know the files exist but want to dynamically load one out of many, use the concat function and a choose/when block:
<xsl:variable name="page_name">
<xsl:choose>
<xsl:when test="$page = 1">
<xsl:value-of select="'page1.xml'"/>
</xsl:when>
<xsl:when test="$page = 2">
<xsl:value-of select="'page2.xml'"/>
</xsl:when>
<!--...-->
<xsl:when test="$page = 9">
<xsl:value-of select="'page9.xml'"/>
</xsl:when>
<xsl:when test="$page = 10">
<xsl:value-of select="'page10.xml'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'page0.xml'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="agree" select="document(concat('include/pages/',$page_name))//processing-instruction()"/>
References
XML External Entity (XXE) Processing
For anyone who needs plain XSLT solution for checking if file exist you can just use
unparsed-text-available(concat(system-property('user.dir'),'/filename.text'))
or wrap it into a function
<xsl:function name="functx:file-exists" xmlns:functx="http://www.functx.com">
<xsl:param name="path"/>
<xsl:value-of select="unparsed-text-available(concat(system-property('user.dir'),'/',$path))"/>
</xsl:function>
using this unparsed-text-available(concat(system-property('user.dir'),'/filename.text'))=false() will mean that file does not exist.
Have fun!