XSLT and document()/Node

D

Daniel Frey

Hello,

I've got a problem. Either I have missed the most important point or
it really does not work:

I want to use the document function with an absolut path on WindowsXP
Professional and Saxon 8.1. However, retrieving a subnode of the
documents content is not possible. Inserting a whole document result
set is functional, subset not. Example:

001 <?xml version="1.0"?>
002 <xsl:stylesheet version="1.1"
003 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
004 <xsl:param name="files" select="''"/>
005 <xsl:template match="/">
006 <Service>
007 <xsl:call-template name="concatenateFiles">
008 <xsl:with-param name="theFiles"
009 select="concat(normalize-space($files), ' ')"/>
010 </xsl:call-template>
011 </Service>
012 </xsl:template>
013
014 <xsl:template name="concatenateFiles">
015 <xsl:param name="theFiles"/>
016 <xsl:variable name="file"
017 select="substring-before($theFiles, ' ')"/>
018 <xsl:copy-of select="document($file)/Service/Output"/>
019 <xsl:variable name="rest"
020 select="substring-after($theFiles, ' ')"/>
021 <xsl:if test="$rest">
022 <xsl:call-template name="concatenateFiles">
023 <xsl:with-param name="theFiles" select="$rest"/>
024 </xsl:call-template>
025 </xsl:if>
026 </xsl:template>
027 </xsl:stylesheet>

Passing an existing file to the "files" parameter does not work at
all. A simple <Service/> is returned. Reducing line 021 to

018 <xsl:copy-of select="document($file)"/>

does work, however doesn't achive the desired result to incorporate
just a subnode of the document loaded.

I am thankful for any hints in this concern.

Daniel Frey
 
D

David Carlisle

018 <xsl:copy-of select="document($file)/Service/Output"/>
018 <xsl:copy-of select="document($file)"/>
does work,

presumably your documents don't have top level element Service with
children Output (perhaps they have elements in some namespace?)

Incidentally since you are using saxon 8 and so xslt2 you needn't step
through that space separated list of filenames using recursion on
substring; you can coerce it directly to a sequence of strings and then
iterate over the sequence.

David
 
D

Daniel Frey

Thanks for your reply. The xml in question has got a <Service> element.
And your right, there is a namespace in the element.

The abstracted xml source look like this:

<?xml version="1.0" encoding="ISO-8859-1"?>
<Service xmlns="uri://bw.ubs.com/map">
<Header ...>
<Status ...>
<Output ...>
</Service>

How do I address the nodes in a namespace attributed element?

I do not understand the other hint you gave me completely. Can you give
a small sample of "coercing" the string list directly?

Thank you for your help.

Daniel Frey
 
D

David Carlisle

an Xpath of /Service/... matches the element with empty namespace URI
and local name Service, you want to match an element with namespace URI
uri://bw.ubs.com/map and local name Service (incidentally I don't think
there is a registered uri scheme of uri: is there? Not that The
Namespace spec cares if the namespace URI is valid as a URI.

In XSLT1 (and this also works in 2) you need to use prefixed expressions
in the xpath, stick
xmlns:x="uri://bw.ubs.com/map"
on your xsl:stylesheet and then use
/x:Service/x:Output

(You don't need to change your source documents)

Since you are using the XSLT2 draft you could instead change the default
Xpath element name namespace by putting
xpath-default-namespace="uri://bw.ubs.com/map"
on your xsl:stylesheet element, but that will affect _all_ Xpath's in
the stylesheet so if some of your input is not in that namespace this
might not be what you want.
I do not understand the other hint you gave me completely. Can you give
a small sample of "coercing" the string list directly?

You could use the as attribute on the xsl:param to force it to be
interpretted as a sequence of strings, or more explictly, gven the white
space separated string you could do

<xsl:for-each select="tokenize($files,'\s+')">
<xsl:copy-of select="doc(.)/....

see
http://www.w3.org/TR/xquery-operators/#func-tokenize
David
 
D

Daniel Frey

Thank you very much David. I copied the namespace solution, perfect! The
tokenize solution is also easy to understand. It simplifies the
stylesheet enormously!

However, I get an exception in saxon 8.1 driver when running the code.
The stylesheet looks now like this:

001 <?xml version="1.0"?>
002 <xsl:stylesheet version="1.1"
003 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
004 xmlns:u="uri://bw.ubs.com/map">
005 <xsl:eek:utput indent="yes"/>
006 <xsl:param name="files" select="''"/>
007 <xsl:template match="/">
008 <Service>
009 <xsl:for-each select="tokenize($files,'\s+')">
010 <xsl:copy-of select="document(.)/u:Service"/>
011 </xsl:for-each>
012 </Service>
013 </xsl:template>
014 </xsl:stylesheet>

But line 009 throws a ClassCastException.

java.lang.RuntimeException: net.sf.saxon.value.StringValue
java.lang.ClassCastException: net.sf.saxon.value.StringValue
at
com.exln.stylus.CSaxon8Driver.startCurrentItem(CSaxon8Driver.java:1100)
at net.sf.saxon.instruct.ForEach.processLeavingTail(ForEach.java:125)
at net.sf.saxon.instruct.Instruction.process(Instruction.java:91)
at
net.sf.saxon.instruct.TraceWrapper.processLeavingTail(TraceWrapper.java:
79)
at net.sf.saxon.instruct.Instruction.process(Instruction.java:91)
at
net.sf.saxon.instruct.InstructionWithChildren.processChildren(Instructio
nWithChildren.java:158)
at
net.sf.saxon.instruct.ElementCreator.processLeavingTail(ElementCreator.j
ava:122)
at net.sf.saxon.instruct.Instruction.process(Instruction.java:91)
at
net.sf.saxon.instruct.TraceWrapper.processLeavingTail(TraceWrapper.java:
79)
at
net.sf.saxon.instruct.InstructionWithChildren.processChildrenLeavingTail
(InstructionWithChildren.java:196)
at net.sf.saxon.instruct.Block.processLeavingTail(Block.java:117)
at net.sf.saxon.instruct.Instruction.process(Instruction.java:91)
at
net.sf.saxon.instruct.TraceWrapper.processLeavingTail(TraceWrapper.java:
79)
at net.sf.saxon.instruct.Template.expand(Template.java:98)
at net.sf.saxon.instruct.Template.processLeavingTail(Template.java:82)
at
net.sf.saxon.instruct.ApplyTemplates.applyTemplates(ApplyTemplates.java:
226)
at net.sf.saxon.Controller.transformDocument(Controller.java:1094)
at net.sf.saxon.Controller.transform(Controller.java:940)
at com.exln.stylus.CSaxon8Driver.doProcessing(CSaxon8Driver.java:169)
at com.exln.stylus.CProcessorDriver.process(CProcessorDriver.java:55)


The parameter passed (without line breaks) is

C:/DOKUME~1/DANIEL~1/LOKALE~1/Temp/test42553/workflow/work42555.2
C:/DOKUME~1/DANIEL~1/LOKALE~1/Temp/test42553/workflow/work42555.4
C:/DOKUME~1/DANIEL~1/LOKALE~1/Temp/test42553/workflow/work42555.6

But I still have the fallback solution I used before. Anyway, a big
thank you for your help!

Daniel Frey
 
D

David Carlisle

If you get a java exception raised by saxon (or any xslt engine) 'tis a
bug in the processor. If you pass that on to Michael at saxon's help
email address I'm sure he'll look at it.

I'm surprised that saxon's document() function accepts
C:/DOKUME~1/DANIEL~1/LOKALE~1/Temp/test42553/workflow/work42555.2
(however you split that off your string) as it is supposed to be a URI
and C: isn't known uri scheme (which needs to be file: or http: or ftp:
or some such) that should be
file:///C:/DOKUME~1/DANIEL~1/LOKALE~1/Temp/test42553/workflow/work42555.2
however I doubt that is the cause of your exception.

David
 
J

Joris Gillis

does work, however doesn't achive the desired result to incorporate
just a subnode of the document loaded.

Hi,

try: 'document($file)//Service' in stead of 'document($file)/Service'. In practical situations, I've epercienced that '/' fails to work when handling a document node. I'm not sure why, though.

regards,
 
D

David Carlisle

Joris Gillis said:
try: 'document($file)//Service' in stead of
'document($file)/Service'. In practical situations, I've epercienced
that '/' fails to work when handling a document node. I'm not sure
why, though.

regards,

If you know that Service is the top level element then // is an
expensive thing to try since it tells the system to search the entire
document to arbitrary depth looking for all Service' elements.

If Service' is not the top level element then clearly /Service won't
work but its usually better to use a more explict path than // so as to
limit searching, eg /*/Service if it's a second level element.

David
 
D

Daniel Frey

In my case it is not suitable to use //Service as the Output element
contains another Service element which should not be incorporated. And I
have the impression that this would not solve the namespace issue, would
it? In my case I would use something like //u:Service to address all
Service elements, wouldn't I?

Daniel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top