getting attributes of all the parent in xslt - java

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>

Related

Conditionaly assign variable to match path in xslt template

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>

XSLT How to iterate a Java List with XALAN

I am trying to iterate over an array using XSLT in order to get a final text looking like this:
array1.firstElement
array1.secondElement
I have this in my XSLT:
<xsl:param name="nombreEmpresaDebe" />
And I put this in my transform method:
transformer.setParameter("nombreEmpresaDebe", listaNombreEmpresaDebe);
renglones/reglon is the name of my node and I am already iterating over it with the following code:
<xsl:for-each select='renglones/renglon'>
<label>
<xsl:value-of select="rubro" />
</label>
<label>
<xsl:value-of select="$nombreEmpresaDebe"/>
</label>
<xsl:for-each>
Now I have to get $nombreEmpresaDebe elements which is a java.util.List but I have no clue about doing it in the same for-each. Does anyone know how to do it?
I need something like this:
<renglon1>
<rubro>rubro1</rubro>
<nombreEmpresaDebe>firstElement</nombreEmpresaDebe>
</renglon1>
<renglon2>
<rubro>rubro2</rubro>
<nombreEmpresaDebe>secondElement</nombreEmpresaDebe>
</renglon2>
You can accomplish this by calling a recursive template. For example
<xsl:call-template name="outputList">
<xsl:with-param name="list" select="$nombreEmpresaDebe"/>
<xsl:with-param name="index" select="0"/>
</xsl:call-template>
Where the template is defined like
<xsl:template name="outputList">
<xsl:param name="list"/>
<xsl:param name="index"/>
<xsl:if test="number($index) < java:size($list)">
<label>
<xsl:value-of select="$nombreEmpresaDebe"/>
</label>
<xsl:call-template name="outputList">
<xsl:with-param name="list" select="$list"/>
<xsl:with-param name="index" select="number($index)+1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
I should note that my answer does not combine this with the iteration of the other nodelist you are handling, but this is the basic List iterating technique that could be combined with that. Also, I realize this is 3 years late, but someone else my find this question and benefit from having an answer.

Need custom order in flat file from XML using XSLT

I want to create a flat file from an xml using xslt with this output in the order as shown:
NM1*CC*1*Smith*John****34*999999999~
N3*100 Main Street~
From this XML:
<Claim>
<Claimant
lastName="Smith"
firstName="John"
middleName=""
suffixName=""
indentificationCodeQualifier="34"
identificationCode="999999999">
<ClaimantStreetLocation
primary="100 Main Street"
secondary=""/>
</Claimant>
</Claim>
With the XSLT I created I get the output in the reversed desired order as shown below due to the nature of how XSLT works as it traverses the input tree I'm assuming:
N3*100 Main Street~
NM1*CC*1*Smith*John****34*999999999~
What do I need to change/add to get the order I am looking for to the XSLT I've written as shown:
`
<xsl:template match="Claim/Claimant">
<xsl:apply-templates />
<xsl:text>NM1*CC*1*</xsl:text>
<xsl:value-of select="#lastName" />
<xsl:text>*</xsl:text>
<xsl:value-of select="#firstName" />
<xsl:text>*</xsl:text>
<xsl:value-of select="#middleName" />
<xsl:text>*</xsl:text>
<xsl:text>*</xsl:text>
<xsl:value-of select="#suffixName" />
<xsl:text>*</xsl:text>
<xsl:value-of select="#indentificationCodeQualifier" />
<xsl:text>*</xsl:text>
<xsl:value-of select="#identificationCode" />
<xsl:text>~</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="Claim/Claimant/ClaimantStreetLocation">
<xsl:apply-templates />
<xsl:text>N3*</xsl:text>
<xsl:value-of select="#primary" />
<xsl:text>~</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>`
Is there a way to do this without combining the two tags into one?
Any feedback would be appreciated.
I don't know if it matters, but I'm using xalan-java to process the xslt in code.
If you want to process the parent before the children, you should move apply-templates to the end of your parent template:
<xsl:template match="Claim/Claimant">
<xsl:text>NM1*CC*1*</xsl:text>
<xsl:value-of select="#lastName" />
<xsl:text>*</xsl:text>
<xsl:value-of select="#firstName" />
<xsl:text>*</xsl:text>
<xsl:value-of select="#middleName" />
<xsl:text>*</xsl:text>
<xsl:text>*</xsl:text>
<xsl:value-of select="#suffixName" />
<xsl:text>*</xsl:text>
<xsl:value-of select="#indentificationCodeQualifier" />
<xsl:text>*</xsl:text>
<xsl:value-of select="#identificationCode" />
<xsl:text>~</xsl:text>
<xsl:text>
</xsl:text>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="ClaimantStreetLocation">
<xsl:apply-templates />
<xsl:text>N3*</xsl:text>
<xsl:value-of select="#primary" />
<xsl:text>~</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>`
Update: What's happening here is:
The first element that is processed is Claim, but there are no templates matching it, so the default template applies, which process the templates for its children nodes.
There, the first child is Claimant, and you do have a template that matches it, so it is applied.
Next, that template is processed in order. But the critical point is that apply-templates omits the attributes in its default select (see
What is the default select of XSLT apply-templates?), so the only matched node there is the ClaimantStreetLocation element.
Given that you have a template that matches ClaimantStreetLocation, it is applied. So, if you want to process first the attributes, you should delay the apply-templates until they are selected, in your case, manually.

xsl variable initialization, method doesn't get called

I'm new to xslt and have a question.
I have a validate class that contains all necessary setters and getters. For example it has such method:
public void setProducer(String producer) {
this.producer = producer;
System.out.println("TEST");
}
When I start my app I see that this method was not called.
I only see in console my test message, when I add in my xsl file such code:
<xsl:value-of name="producerName" />
So where is my mistake or xsl:variable initialized during first use?
I have this code:
<xsl:param name="category-name" />
<xsl:param name="subcategory-name" />
<xsl:param name="producer" />
<xsl:param name="model" />
<xsl:param name="color" />
<xsl:param name="date_of_issue" />
<xsl:param name="price" />
<xsl:param name="not_in_stock" />
<xsl:variable name="validator" select="validation:new()" />
<xsl:template match="/">
<xsl:for-each select="products/category">
<xsl:if test="name() = 'category' and #name=$category-name">
<xsl:apply-templates select="subcategory[#name=$subcategory-name]" />
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="subcategory">
<xsl:apply-templates select="good" />
<xsl:variable name="errorSize"
select="validation:getErrorListSize($validator)" />
<xsl:if test="$errorSize = 0">
<xsl:variable name="producerName"
select="validation:setProducer($validator, $producer)" />
<xsl:variable name="setModel"
select="validation:setModel($validator, $model)" />
<xsl:variable name="setColor"
select="validation:setColor($validator, $color)" />
<xsl:variable name="setDateOfIssue"
select="validation:setDateOfIssue($validator, $date_of_issue)" />
<xsl:if test="$not_in_stock != null">
<xsl:variable name="setPrice"
select="validation:setPrice($validator, $price)" />
</xsl:if>
<xsl:variable name="validationResult"
select="validation:validateAllFields($validator)" />
VALIDATION FINISHED
<xsl:variable name="errors"
select="validation:getErrorListSize($validator)" />
<xsl:value-of select="$errors"/>
</xsl:if>
<xsl:if test="$errorSize != 0">
REDIRECT. ERROR EXISTS
</xsl:if>
</xsl:template>
First point: extension functions in XSLT are highly product-dependent, so you can't really ask us this question without telling us what XSLT processor you are using.
Mixing a declarative language and a procedural language is always going to be tricky (and the best thing would be to avoid it). Certainly in XSLT an optimizing processor will evaluate variables lazily, which means that if a variable isn't referenced then it won't be evaluated.
In Saxon you can generally get away with calling Java methods that have side-effects if you "pretend" to use the result of the method in your result tree; you can achieve that by calling the method from an xsl:value-of instruction.
Some XSLT processors such as Saxon use lazy evaluation, so the variable will not be evaluated untill the it's actually needed.
So, u haven't use such approach, there are other possibilities to call methods.
If variable is useless in your xsl document, use something like this instead:
<xsl:value-of select="validation:setProducer($validator, $producer)"/>

how to create hyperlinks when transforming xml to xsl-fo using xlst?

I would like to transform any http/s based url inside random text to be automatically tagged with xsl-fo , where the random text might contains one or more http/s based url.
So the http/s url is not part of an attribute or the only content of a node, but part of text within a node.
For example: the source
<misc>
<comment>Yada..yada..yadda, see http://www.abc.com.
Bla..bla..bla.. http://www.xyz.com</comment>
</misc>
Will be transform into something like:
<fo:block>
Yada..yada..yadda, see <fo:basic-link external-destination="http://www.abc.com">http://www.abc.com</fo:basic-link>.
Bla..bla..bla.. <fo:basic-link external-destination="http://www.xyz.com">http://www.xyz.com</fo:basic-link>
<fo:/block>
The library we are using is Apache FOP and Xalan-J.
If you have to use a pure XSLT method, you could use this:
<xsl:template match="comment">
<fo:block>
<xsl:call-template name="dig-links">
<xsl:with-param name="content" select="."/>
</xsl:call-template>
</fo:block>
</xsl:template>
<xsl:template name="dig-links">
<xsl:param name="content" />
<xsl:choose>
<xsl:when test="contains($content, 'http://')">
<xsl:value-of select="substring-before($content, 'http://')"/>
<xsl:variable name="url" select="concat('http://', substring-before(substring-after(concat($content, ' '), 'http://'), ' '))"/>
<fo:basic-link>
<xsl:attribute name="external-destination">
<xsl:choose>
<xsl:when test="substring($url, string-length($url), 1) = '.'">
<xsl:value-of select="substring($url, 1, string-length($url)-1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$url"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:value-of select="$url"/>
</fo:basic-link>
<xsl:call-template name="dig-links">
<xsl:with-param name="content" select="substring-after($content, $url)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$content"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
It's not a perfect solution however, thus if you have typos such as two dots at the end of a url, the external-destination attribute will get one.
I don't know what xsl:fo means, but if you have access to the xslt file that actually does the conversion, you might convert it yourself by using xml string functions mentioned here. If you have access to the transformed output only, you still can
apply another xslt that does what you want or
transform it yourself using SAX and using the java regex functions

Categories

Resources