Need custom order in flat file from XML using XSLT - java

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.

Related

getting attributes of all the parent in xslt

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>

XSL - using variables in nodepath

I have a xsl as below
<xsl:variable name="foo" select="concat('some','stuff')" />
<xsl:if test="$foo">
<xsl:element name="hello">
<xsl:attribute name="id">
<xsl:value-of select="-1"/>
</xsl:attribute>
<xsl:element name="region">
<xsl:value-of select="$foo/child"/> <!-- foo is variable, but always has a 'child' node -->
</xsl:element>
</xsl:element>
</xsl:if>
I get the below output:
<hello id="-1">
and an exception:
java.lang.ClassCastException: org.apache.xpath.objects.XString incompatible with
org.apache.xpath.objects.XNodeSet
what am I doing wrong?
<xsl:variable name="foo" select="concat('some','stuff')" />
will create a string with value 'somestuff'.
The string turns your line
<xsl:value-of select="$foo/child"/>
effectively into
<xsl:value-of select="'somestuff'/child"/>
which is not a valid XPath expression.
Thie string cannot be used in any node expressions, only in string operations.
Replace your
<xsl:variable name="foo" select="concat('some','stuff')" />
by something like
<xsl:variable name="foo" select="./somestuff" />
which returns a node.

XSLT 1: XPath - select/extract a number from a string, with default value

I'm transforming a document using the XSL engine provided by out-of-the-box Java 6.
I need to grab a value from an attribute, and use it as an index attribute in a document I am transforming to.
For example, the element:
<myElem bogen="K [1]"/>
the attribute 'bogen' may have values:
"K [1]"
"K [2]"
up to 'n' values.
It may also simply have
"K" to indicate the same as "K [1]"
the corresponding transformation result (from "K [1]" would look like this:
<myTransformedElem index="1"/>
I could use the transformation:
<xsl:value-of select="translate(#bogen,translate(#bogen, '0123456789', ''), '')"/>
if it were just values expected as "K [1]".
But where there is the possibility of having no actual number included, such as "K", this stuffs me up.
The only way I can do it is if I abuse the 'number' function, expect a 'NaN' and translate that. Which seems exceedingly ugly.
For example:
<xsl:value-of select="translate(string(number(translate(#bogen,translate(#bogen, '0123456789', ''), ''))), 'aN', '1')"/>
gets me the correct result each time.
Is there a better, nicer way to do this in XSLT without resorting to custom methods (i.e., import static java methods) ?
For example; were I to need a default value of a multi-character string, I couldn't use the above solution.
thanks in advance.
sean
Here's one way to do this:
<xsl:template name="GetIndex">
<xsl:param name="value" />
<xsl:param name="default" select="1" />
<xsl:variable name="foundIndex"
select="substring-before(
substring-after($value, '['), ']')" />
<xsl:choose>
<xsl:when test="$foundIndex">
<xsl:value-of select="$foundIndex"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This call produces the value 1:
<xsl:call-template name="GetIndex">
<xsl:with-param name="value" select="'K[1]'" />
</xsl:call-template>
This call produces the value 37:
<xsl:call-template name="GetIndex">
<xsl:with-param name="value" select="'K[37]'" />
</xsl:call-template>
This call produces the value 1:
<xsl:call-template name="GetIndex">
<xsl:with-param name="value" select="'K'" />
</xsl:call-template>
This call produces the value 999:
<xsl:call-template name="GetIndex">
<xsl:with-param name="value" select="'K'" />
<xsl:with-param name="default" select="999" />
</xsl:call-template>
If you find the xsl:choose to be too verbose, you can rewrite GetIndex like this and get the same results:
<xsl:template name="GetIndex">
<xsl:param name="value" />
<xsl:param name="default" select="1" />
<xsl:variable name="foundIndex"
select="substring-before(
substring-after($value, '['), ']')" />
<xsl:value-of select="concat($foundIndex,
substring($default, 1,
not($foundIndex) *
string-length($default)))"/>
</xsl:template>
Personally, I think the version with xsl:choose is clearer, even if it is longer.
with this XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="myElem">
<xsl:choose>
<xsl:when test="translate(#bogen, '0123456789', '') != #bogen">
<myTransformedElem index="{translate(#bogen,translate(#bogen, '0123456789', ''), '')}"/>
</xsl:when>
<xsl:otherwise>
<myTransformedElem index="1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied to this XML:
<root>
<myElem bogen="K"/>
</root>
produces
<myTransformedElem index="1"/>
and when applied to this XML:
<root>
<myElem bogen="K [2]"/>
</root>
produces
<myTransformedElem index="2"/>

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