I am writing a java program where in some cases I have to perform a xslt transformation.
In this case i need to add an attribute named type at the level of the work.
Its value should be the same as the value of the element ns2:type_work
For example:
<ns2:work>
<ns2:type_work>PROP</ns2:type_work>
<ns2:identifier_work>2017/375/943030</ns2:identifier_work>
<ns2:work>
should become
<ns2:work type="PROP">
<ns2:type_work>PROP</ns2:type_work>
<ns2:identifier_work>2017/375/943030</ns2:identifier_work>
<ns2:work>
I have made the following XSLT
<xsl:template match="ns2:work">
<ns2:work>
<xsl:attribute name="type" select="ns2:type_work/node()" />
<xsl:apply-templates select="#*|child::node()" />
</ns2:work>
</xsl:template>
and I apply it using the proper java functios (javax.xml.transform.),
I get no erros,
the attribute -type- is created
but it is empty.
Does it have to do something with the XSLT version is my xslt not compatible with 1.0?
How can I bypass this?
If you are using XSLT 1.0, then the code needs to look like this, as select is not valid on xsl:attribute in XSLT 1.0
<xsl:attribute name="type">
<xsl:value-of select="ns2:type_work/node()" />
</xsl:attribute>
(Note that you can just do <xsl:value-of select="ns2:type_work" /> here)
Better still, use Attribute Value Templates
<ns2:work type="{ns2:type_work}" />
<xsl:apply-templates select="#*|child::node()" />
</ns2:work>
Related
I want to dynamically build match in xslt template. I use xls fo and apache fop and saxon9he. In best way I want to pass param from java but firstly I try set this in xslt.
When i create variable example like this:
<xsl:variable name="testPath" select="/abc:Files/def:Description" />
also work correctly if I try use this in apply-templates:
<xsl:apply-templates select="$testPath/qwe:Test"/>
But i want dynamically set testPath variable. I try use choose tag:
<xsl:variable name="testPath">
<xsl:choose>
<xsl:when test="$versionNo = '2'">
<xsl:value-of select="/abc:Files/def:Description" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="/abc:Files/def:Names" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
But this approach not working and when I try use this variable:
<xsl:apply-templates select="$testPath/qwe:Test"/>
I get this error:
Error evaluating ((attr{table-layout=...}, ...)) on line 82 column 21
of pdf_gen.xsl: SXCH0003: org.apache.fop.fo.ValidationException:
"fo:table-body" is missing child elements. Required content model:
marker* (table-row+|table-cell+) (See position 82:21):
file:/C:/Users/SuperUser/workspace/project/xls-editor/target/classes/pdf/xsl/pdf_gen.xsl:82:21:
"fo:table-body" is missing child elements. Required content model:
marker* (table-row+|table-cell+) (See position 82:21)
In best option I want to pass $testPath variable from Java as param, example:
transformer.setParameter("testPath ", "/abc:Files/def:Description");
and use in xslt
<xsl:param name="testPath "/>
and apply in templates:
<xsl:apply-templates select="$testPath/qwe:Test"/>
but i get following error:
Type error evaluating ($testPath) in
xsl:apply-templates/#select on line 74 column 60 of
pdf_gen.xsl: XPTY0019: The required item type of the
first operand of '/' is node(); the supplied value
u"/abc:Files/def:Description" is an atomic value
Why is not any solution working how to achieve it?
As you use Saxon 9 you do use at least XSLT 2 with XPath 2 and there I would suggest to implement the variable selection in XPath with e.g.
<xsl:variable name="testPath" select="if ($versionNo = '2') then /abc:Files/def:Description else /abc:Files/def:Names"/>
That way your
<xsl:apply-templates select="$testPath/qwe:Test"/>
should work fine to process the qwe:Test child element(s) of the previously selected def:Description or def:Names elements.
It is of course also possible to use e.g.
<xsl:apply-templates select="(if ($versionNo = '2') then /abc:Files/def:Description else /abc:Files/def:Names)/qwe:Test"/>
or do e.g.
<xsl:apply-templates select="(/abc:Files/def:Description[$versionNo = '2'], /abc:Files/def:Names[$versionNo != '2'])/qwe:Test"/>
I'm dealing with a similar problem.
It seems to me that variables visibility is only inside the <xsl:choose> block and I could suggest you to make templates blocks and to call them in this way:
<xsl:choose>
<xsl:when test="$versionNo = '2'" >
<xsl:call-template name="TemplateOne">
<xsl:with-param name="testPath" select="'/abc:Files/def:Description'" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="TemplateTwo">
<xsl:with-param name="testPath" select="'/abc:Files/def:Names'" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
with templates definition as:
<xsl:template name="TemplateOne">
<xsl:param name="testPath" />
...
bla bla bla
remember to use variable in this template as "{$testPath}"
...
</xsl:template>
I'm using Saxon HE 9.7.0 to transform some XSLT 2.0. In my file I'm trying to select a sub-tree and put it in a variable to use it later on.
Input XML:
<?xml version="1.0"?>
<documents>
<document>
<code>009190</code>
<index>3</index>
</document>
<document>
<code>583876</code>
<index>1</index>
</document>
<document>
<code>277410</code>
<index>2</index>
</document>
</documents>
Now in my XSLT I select the document with the lowest index and want to use its code later (the variable documents contains the root tree):
<xsl:variable name="selectedDocument">
<xsl:variable name="lowestDocumentIndex" select="min($documents/document/orderIndex)" />
<xsl:value-of select="$documents/documents[index=$lowestDocumentIndex]" />
</xsl:variable>
<!-- Now later on, I want to use the contents of the selected document: -->
<xsl:value-of select="$selectedDocument/code" />
This causes an exception in the parser which looks like this:
Caused by: java.lang.RuntimeException: Internal error evaluating template rule at line 23 in module
at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(TemplateRule.java:366)
at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:456)
at net.sf.saxon.Controller.transformDocument(Controller.java:2291)
at net.sf.saxon.Controller.transform(Controller.java:1863)
at net.sf.saxon.s9api.XsltTransformer.transform(XsltTransformer.java:579)
at net.sf.saxon.jaxp.TransformerImpl.transform(TransformerImpl.java:185)
at de.haba.genex.exp.core.model.nodes.XSLTTransformationNode.lambda$process$0(XSLTTransformationNode.java:146)
... 40 more
Caused by: java.lang.ClassCastException: net.sf.saxon.value.UntypedAtomicValue cannot be cast to net.sf.saxon.om.NodeInfo
at net.sf.saxon.expr.SimpleStepExpression.iterate(SimpleStepExpression.java:108)
at net.sf.saxon.expr.Atomizer.iterate(Atomizer.java:293)
at net.sf.saxon.expr.AtomicSequenceConverter.iterate(AtomicSequenceConverter.java:249)
at net.sf.saxon.expr.SystemFunctionCall.evaluateArguments(SystemFunctionCall.java:360)
at net.sf.saxon.expr.FunctionCall.iterate(FunctionCall.java:544)
at net.sf.saxon.expr.Expression.evaluateItem(Expression.java:763)
at net.sf.saxon.expr.Expression.evaluateAsString(Expression.java:859)
at net.sf.saxon.expr.instruct.SimpleNodeConstructor.processLeavingTail(SimpleNodeConstructor.java:186)
at net.sf.saxon.expr.instruct.ValueOf.processLeavingTail(ValueOf.java:283)
at net.sf.saxon.expr.instruct.Block.processLeavingTail(Block.java:653)
at net.sf.saxon.expr.LetExpression.processLeavingTail(LetExpression.java:711)
at net.sf.saxon.expr.instruct.Block.processLeavingTail(Block.java:653)
at net.sf.saxon.expr.LetExpression.processLeavingTail(LetExpression.java:711)
at net.sf.saxon.expr.instruct.Block.processLeavingTail(Block.java:653)
at net.sf.saxon.expr.LetExpression.processLeavingTail(LetExpression.java:711)
at net.sf.saxon.expr.instruct.Block.processLeavingTail(Block.java:653)
at net.sf.saxon.expr.instruct.NamedTemplate.expand(NamedTemplate.java:257)
at net.sf.saxon.expr.instruct.CallTemplate.process(CallTemplate.java:356)
at net.sf.saxon.expr.LetExpression.process(LetExpression.java:568)
at net.sf.saxon.expr.instruct.ForEach.processLeavingTail(ForEach.java:453)
at net.sf.saxon.expr.LetExpression.processLeavingTail(LetExpression.java:711)
at net.sf.saxon.expr.instruct.Choose.processLeavingTail(Choose.java:835)
at net.sf.saxon.expr.instruct.Instruction.process(Instruction.java:149)
at net.sf.saxon.expr.LetExpression.process(LetExpression.java:568)
at net.sf.saxon.expr.instruct.ForEach.processLeavingTail(ForEach.java:453)
at net.sf.saxon.expr.LetExpression.processLeavingTail(LetExpression.java:711)
at net.sf.saxon.expr.instruct.Block.processLeavingTail(Block.java:653)
at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(TemplateRule.java:353)
... 46 more
With XSLT 2.0 no more storing RTFs but keeping real temporary trees, shouldn't this work?
This is just a cut-down example of what I'm trying to do, I'm actually having a <xsl:choose> within my <xsl:variable> and the document structure is more complex, but the idea should be the same.
EDIT: It seems that my problem is related to that I pass the list of documents to another template first. Using the xsl:copy-of as written in the answers seems to work, but not when I do the following:
(Try it out on http://xsltransform.net/ejivdHL/2)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<xsl:call-template name="processDocuments">
<xsl:with-param name="docs" select="documents" />
</xsl:call-template>
</xsl:template>
<xsl:template name="processDocuments">
<xsl:param name="docs" />
<xsl:variable name="selectedDocument">
<xsl:variable name="lowestDocumentIndex" select="min(doc/document/index)" />
<xsl:copy-of select="doc/document[index=$lowestDocumentIndex]" />
</xsl:variable>
<xsl:value-of select="$selectedDocument/document/code" />
</xsl:template>
</xsl:transform>
Any more ideas of what's going wrong? I need to use a second template for this, because I use the template multiple times for various nodes.
To store a copy of nodes you need <xsl:copy-of select="$documents/documents[index=$lowestDocumentIndex]" /> instead of <xsl:value-of select="$documents/documents[index=$lowestDocumentIndex]" />. value-of creates a text node with the string value(s) of the selected node(s).
Furthermore, some references/paths don't seem to matched, when fixing that I get
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<xsl:variable name="selectedDocument">
<xsl:variable name="lowestDocumentIndex" select="min(documents/document/index)" />
<xsl:copy-of select="documents/document[index=$lowestDocumentIndex]" />
</xsl:variable>
<!-- Now later on, I want to use the contents of the selected document: -->
<xsl:value-of select="$selectedDocument/document/code" />
</xsl:template>
</xsl:transform>
outputting 583876 at http://xsltransform.net/ejivdHL.
There is really no need to create a temporary tree, you could simply select an original input element:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<xsl:variable name="lowestDocumentIndex" select="min(documents/document/index)" />
<xsl:variable name="selectedDocument" select="documents/document[index=$lowestDocumentIndex]" />
<!-- Now later on, I want to use the contents of the selected document: -->
<xsl:value-of select="$selectedDocument/code" />
</xsl:template>
</xsl:transform>
Online at http://xsltransform.net/ejivdHL/1.
Use:
<xsl:variable name="lowestDocumentIndex" select="min($documents/document/index)" />
<xsl:variable name="selectedDocument" select="$documents/document[index=$lowestDocumentIndex]" />
It seems that the <xsl:value-of> in your code extracted the value of a selected node (concatenated text nodes) not the node itself.
By the way: Your code contains $documents/documents.
Note that the last s should be removed.
A stacktrace saying "Saxon internal error" means it's a Saxon bug. Possibly one we already know about and may have fixed, but a bug all the same. It's best to report Saxon bugs on the bug tracker at saxonica.plan.io. We will almost certainly need a "repro" - that is, a stylesheet and source document, and any other necessary details like configuration parameters, that enable us to reproduce the problem.
Just to give a little bit of preliminary analysis, the key failure is
java.lang.ClassCastException: net.sf.saxon.value.UntypedAtomicValue cannot be cast to net.sf.saxon.om.NodeInfo
at net.sf.saxon.expr.SimpleStepExpression.iterate(SimpleStepExpression.java:108)
That tells us that it's a run-time error and that we're evaluating a "simple step expression" which is something like $lhs/child::X where the LHS is an expression returning a singleton, and the RHS is an axis expression. I can tell from the line number that you aren't executing the latest maintenance release (because the cast to NodeInfo is now on line 110). The ClassCastException occurs because the LHS of the expression is expected to be a node, but is actually an atomic value, which means something has gone wrong in the type checking which should have caught this earlier. It could be an error in your code that we're failing to pick up and diagnose correctly.
Unfortunately a stack trace doesn't give us enough information to tell exactly what code in your stylesheet is executing at the point of failure. That's why we need the repro, so we can run it in a debugging environment and catch it at the point of failure.
That suggests two actions: (a) please try it on the latest Saxon maintenance release (currently 9.7.0.14, but watch this space), and (b) if it crashes again, submit a full repro at saxonica.plan.io. Thanks!
I have an XML (I know it is incorrect as per XML standards but I am restricted to changes as I am processing response from external party) as follows,
XML Snippet : <root>
<3party>some_value</3party>
</root>
I would like to fetch <3party> from above snippet in XSL stylesheet transformation. The fact is the <3party> element is invalid so I can no refer it as follows as it fails the xsl compilation. I would need a way to refer it as a partial element may be using some regx way? Following is incorrect.
<xsl:variable select="$response/root/3party" />
Any answers would help me out.
Edit : Possible solution to above usecase would be.
<xsl:for-each select="$response/root">
<!-- Node name -->
<xsl:variable name="name" select="local-name()" />
<!-- check if it contains party -->
<xsl:if test="contains($name, 'party')">
<!-- Node value -->
<xsl:variable name="value" select="node()" />
</xsl:if>
</xsl:for-each>
With regard to the code added to your question:
<xsl:for-each select="$response/root">
<!-- Node name -->
<xsl:variable name="name" select="local-name()" />
<!-- check if it contains party -->
<xsl:if test="contains($name, 'party')">
<!-- Node value -->
<xsl:variable name="value" select="node()" />
</xsl:if>
</xsl:for-each>
The test contains($name, 'party') will never return true. The context node's name is root and "root" does not contain "party".
On a more general note: you seem to think that the problem is how to get your XSLT stylesheet to compile. That is not so. The real problem here is that your input is not well-formed XML. The input must be parsed before it can be transformed, and if it's not well-formed XML, the process will fail well before even considering your stylesheet.
I have following xml structure
<comp name = "a">
<subcomp1 name = "a1">
<subcomp2 name = "a2"/>
</subcomp1>
<subcomp3 name="a3/>
</comp>
If I try the following syntax,
<xsl:value-of select="#name" />
gives attribute value of name when I am in perticular tag. ie when I am at
<comp> #name = a, at<subcomp2> it is a2
But I want to get all the attribute value including the parents. ie when I am at
<subcomp2> I want value a->a1->a2
<subcomp1> a->a1
<subcomp3> a->a3
<xsl:value-of select="..\#name" />
gives only one parent above. So please let me know the solution for the same
The expression you want is ancestor-or-self::*/#name
If you are using XSLT 2.0, then xsl:value-of returns all matching attributes, so you can just do this to list them, for example
<xsl:value-of select="ancestor-or-self::*/#name" separator="-" />
However, in XSLT 1.0, xsl:value-of will only return the value of the first one. So, you can use xsl:for-each instead
<xsl:for-each select="ancestor-or-self::*/#name">
<xsl:if test="position() > 1">-</xsl:if>
<xsl:value-of select="." />
</xsl:for-each>
Or maybe this...
<xsl:for-each select="ancestor-or-self::*">
<xsl:if test="position() > 1">-</xsl:if>
<xsl:value-of select="#name" />
</xsl:for-each>
I want the $content to be the expected string. I know that copy-of instead of value-of $content produces the expected string. But how do I not use copy-of and pass it to say a java extension function?
I asked a different related question here.
XML
<?xml version="1.0"?>
<a>
<b c="d"/>
<b c="d"/>
<b c="d"/>
</a>
XSL
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="foo">
<xsl:param name="content"></xsl:param>
<!-- <xsl:copy-of select="$content"></xsl:copy-of> -->
<!-- copy-of produces the expected string here, but how to pass to Java -->
<xsl:value-of select="java:someMethod($content)" />
<!-- I want the content to be the expected string -->
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="foo">
<xsl:with-param name="content">
<xsl:for-each select="a/b">
<e>
<xsl:value-of select="#c" />
</e>
</xsl:for-each>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Desired String to be passed to Java extension function from $content.
<?xml version="1.0"?>
<e>d</e>
<e>d</e>
<e>d</e>
PS: Calling foo is mandatory.
Ultimately, my objective is to emulate result-document in XSLT 1.0 with extension function.
Calling Java extension functions depends on which XSLT processor you are using, which you haven't told us. If you use Java's TransformerFactory you will get whatever is on the classpath, for example the built-in version of Xalan, the Apache version of Xalan, or Saxon.
Your description suggests you want to pass a string containing lexical XML to your Java extension function. That implies you need to serialize a node to a string. That can't be achieved without an extension function such as saxon:serialize(). It's probably easier to pass the node to your Java method, and have the Java method do the serialization.