I'm transforming some XML into a SQL query, using this template. It does what it should do.
<xsl:template match="link-info">
UNION<br/>
SELECT
<xsl:value-of select="//database-info/#name"/>_<xsl:value-of select="key('Tagkey', forwardreferencetag)"/>.term, thesau_term.term,<br/>
COUNT(<xsl:value-of select="//database-info/#name"/>_<xsl:value-of select="key('Tagkey', forwardreferencetag)"/>.term) AS frequency<br/>
FROM <xsl:value-of select="//database-info/#name"/>_<xsl:value-of select="key('Tagkey', forwardreferencetag)"/><br/>
INNER JOIN thesau_term on <xsl:value-of select="//database-info/#name"/>_<xsl:value-of select="key('Tagkey', forwardreferencetag)"/>.term = thesau_term.priref<br/>
GROUP BY <xsl:value-of select="//database-info/#name"/>_<xsl:value-of select="key('Tagkey', forwardreferencetag)"/>.term, thesau_term.term
<br/>
</xsl:template>
The template generates SELECT clauses for a whole bunch of tables. As you can see, table names are constructed from the XML using:
<xsl:value-of select="//database-info/#name"/>_<xsl:value-of select="key('Tagkey', forwardreferencetag)"/>
which is repeated 5 times. Not DRY. Now, neither xsl:variable nor xsl:param seem to be intended for making things more readable, something like
<xsl:mysnippet name="table_name">
<xsl:value-of select="//database-info/#name"/>_<xsl:value-of select="key('Tagkey', forwardreferencetag)"/>
</xsl:mysnippet>
and then instead of that long clause something like this whenever required.
<xsl:mysnippet name="$table_name" />
Is there any way of doing this within XSLT 1.0?
This is how I might write it in XSLT 3.0
<xsl:template match="link-info" expand-text="yes">
<xsl:variable name="name_tag" select="concat(//database-info/#name,
key('Tagkey', forwardreferencetag))" as="xs:string"/>
UNION<br/>
SELECT {$name_tag}.term, thesau_term.term,<br/>
COUNT({$name_tag}.term) AS frequency<br/>
FROM {$name_tag}><br/>
INNER JOIN thesau_term on {$name_tag}.term = thesau_term.priref<br/>
GROUP BY {$name_tag}.term, thesau_term.term
<br/>
</xsl:template>
With XSLT 1.0 you can't do the curly-brace expand-text thing, but a variable is still quite an improvement:
SELECT <xsl:value-of select="$name_tag"/>.term, thesau_term.term,<br/>
Related
I'm doing an xslt transform that generates a sql statement for me. The way I'm using below is not working. Is there a way?
<xsl:template match="foo">
<xsl:variable name="var1" select="#att_val1" />
select $var1.* from $var1
</xsl:template>
I know it will work if I do this:
<xsl:template match="foo">
select <xsl:value-of select="#att_val1" />.* from <xsl:value-of select="#att_val1" />
</xsl:template>
In XSLT 1.0, variable references are recognized in XPath expressions, but not in general template text. To evaluate an XPath expression and output the result as a text node in the result tree, use xsl:value-of, as you already know how to do. Example:
<xsl:template match="foo">
<xsl:variable name="var1" select="#att_val1" />
select <xsl:value-of select="$var1"/>.* from <xsl:value-of select="$var1"/>
</xsl:template>
Alternatively, you could build the whole select command in one xsl:value-of with use of the concat() function.
Unless you move to XSLT 3.0 (https://www.w3.org/TR/xslt-30/#text-value-templates) where you can do e.g. <xsl:template match="foo" expand-text="yes">select {#att_val1}.* from {#att_val1}</xsl:template> you will have to use your second option or perhaps a <xsl:template match="foo"><xsl:value-of select="concat('select ', #att_val1, '.* from ', #att_val1)"/></xsl:template>, but there is certainly no way in XSLT 1.0 to avoid the use of xsl:value-of completely.
I'm strugling with a Choose statement and the corresponding Test-Clause.
If have the following XML (just an extract) which represents an datamodel exportet from Enterprise Architect as XMI:
<xmi:XMI xmi:version="2.1" xmlns:uml="http://schema.omg.org/spec/UML/2.1" xmlns:xmi="http://schema.omg.org/spec/XMI/2.1" xmlns:thecustomprofile="http://www.sparxsystems.com/profiles/thecustomprofile/1.0" xmlns:EAUML="http://www.sparxsystems.com/profiles/EAUML/1.0"> <xmi:Documentation exporter="Enterprise Architect" exporterVersion="6.5"/> <uml:Model xmi:type="uml:Model" name="EA_Model" visibility="public"> <packagedElement xmi:type="uml:Package" xmi:id="EAPK_F3388CFE_57A7_4d84_8866_3FB3AADE565A" name="Data Model - SQLServer2012" visibility="public">
<packagedElement xmi:type="uml:Artifact" xmi:id="EAID_B62341D4_41C6_4c83_A60A_4CA65C2E185E" name="Database SQLServer2012" visibility="public"/>
<packagedElement xmi:type="uml:Package" xmi:id="EAPK_BA7676C5_40BC_4bd9_A0F5_F6B15E534E8E" name="Logical Model" visibility="public">
<packagedElement xmi:type="uml:Class" xmi:id="EAID_2DC36189_CCFB_40bf_A1CB_CD4FB08FE8B5" name="AnamneseStatus" visibility="public">
<ownedAttribute xmi:type="uml:Property" xmi:id="EAID_9BBF5184_37F8_4729_9DC1_7ED3B4D8FC98" name="RCHIUNET05_ContextKey" visibility="public" isStatic="false" isReadOnly="false" isDerived="false" isOrdered="true" isUnique="false" isDerivedUnion="false">
<lowerValue xmi:type="uml:LiteralInteger" xmi:id="EAID_LI000001_37F8_4729_9DC1_7ED3B4D8FC98" value="1"/>
<upperValue xmi:type="uml:LiteralInteger" xmi:id="EAID_LI000002_37F8_4729_9DC1_7ED3B4D8FC98" value="1"/>
<type xmi:idref="EASQL_Server_2012_nvarchar"/>
</ownedAttribute>
<ownedAttribute xmi:type="uml:Property" xmi:id="EAID_BC1F93D0_A7F4_474c_A27E_26D3ABCCFB7B" name="MRNCmpdId" visibility="public" isStatic="false" isReadOnly="false" isDerived="false" isOrdered="false" isUnique="true" isDerivedUnion="false">
<lowerValue xmi:type="uml:LiteralInteger" xmi:id="EAID_LI000003_A7F4_474c_A27E_26D3ABCCFB7B" value="1"/>
<upperValue xmi:type="uml:LiteralInteger" xmi:id="EAID_LI000004_A7F4_474c_A27E_26D3ABCCFB7B" value="1"/>
<type xmi:idref="EASQL_Server_2012_nvarchar"/>
</ownedAttribute>
..........
So with my XSL I will loop throug the relevant nodes and extract the tablenames and attributes. this works without problem. Now I need to translate the EA datatype into another datatype definition.
Lets say: EASQL_Server_2012_nvarchar needs to become System.String
The XSL doing this looks like this (since there are other datatypes the Choose Statement will be longer than showed here):
<xsl:for-each select="ownedAttribute[#xmi:type='uml:Property']">
<xsl:text disable-output-escaping="yes"><</xsl:text>
<xsl:value-of select="'Element Name="'"/>
<xsl:value-of select="#name"/>
<xsl:value-of select="'" '"/>
<xsl:choose>
<xsl:when test="#xmi:idref = 'EASQL_Server_2012_nvarchar'">
<xsl:value-of select="'Type="'"/>
<xsl:value-of select="'System.String'"/>
<xsl:value-of select="'" '"/>
<xsl:value-of select="'MaxLength="'"/>
<xsl:value-of select="'400'"/>
<xsl:value-of select="'" '"/>
</xsl:when>
<xsl:when test="#xmi:idref = 'EASQL_Server_2012_int'">
<xsl:value-of select="'Type="'"/>
<xsl:value-of select="'System.Int32'"/>
<xsl:value-of select="'" '"/>
</xsl:when>
......
Now my problem is, that it will not hit the test conditions and always run into the "otherwise" statement.
Does somebody see why the test condition is not working?
Thank you for any help on this.
Cheers
Sandro
I suspect that instead of:
<xsl:when test="#xmi:idref = 'EASQL_Server_2012_nvarchar'">
you want to do:
<xsl:when test="type/#xmi:idref = 'EASQL_Server_2012_nvarchar'">
since in the partial example you have posted, ownedAttribute (which is your context node when you run this test) has no xmi:idref attribute, but its child element type does.
P.S. I don't know what you're doing overall, but I cringe whenever I see:
<xsl:text disable-output-escaping="yes"><</xsl:text>
This should never be necessary. If - as it seems - you're not outputting XML, set the output method to text. Then it's not necessary to disable output escaping.
for performance testing purposes I want to take a small XML file and create a bigger one from it - using XSLT. Here I plan to take each entity (Campaign node in the example below) in the original XML and copy it n times, just changing its ID.
The only way I can think of to realize this, is a xsl:for-each select "1 to n". But when I do this I do not seem to be able to access the entity node anymore (xsl:for-each select="campaigns/campaign" does not work in my case). I am getting a processor error: "cannot be used here: the context item is an atomic value".
It seems that by using the "1 to n" loop, I am loosing the access to my actual entity. Is there any XPath expression that gets me access back or does anyone have a completely different idea how to realize this?
Here is what I do:
Original XML
<campaigns>
<campaign id="1" name="test">
<campaign id="2" name="another name">
</cmpaigns>
XSLT I try to use
<xsl:template match="/">
<xsl:element name="campaigns">
<xsl:for-each select="1 to 10">
<xsl:for-each select="campaigns/campaign">
<xsl:element name="campaign">
<xsl:copy-of select="#*[local-name() != 'id']" />
<xsl:attribute name="id"><xsl:value-of select="#id" /></xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
</xsl:element>
</xsl:template>
Define a variable as the first thing in the match, like so:
<xsl:variable name="foo" select="."/>
This defines a variable $foo of type nodeset. Then access it like this
<xsl:for-each select="$foo/campaigns/campaign">
...
</xsl:for-each>
I have a variable in XSLT called variable_name which I am trying to set to 1, if the Product in question has attributes with name A or B or both A & B.
<xsl:variable name="variable_name">
<xsl:for-each select="product/attributes">
<xsl:if test="#attributename='A' or #attributename='B'">
<xsl:value-of select="1"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
Is there any way to match multiple strings using the if statement, as mine just matches if A is present or B is present. If both A & B are present, it does not set the variable to 1. Any help on this would be appreciated as I am a newbie in XSLT.
You can use xsl:choose statement, it's something like switch in common programming languages:
Example:
<xsl:variable name="variable_name">
<xsl:for-each select="product/attributes">
<xsl:choose>
<xsl:when test="#attributename='A'">
1
</xsl:when>
<xsl:when test=" #attributename='B'">
1
</xsl:when>
<!--... add other options here-->
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
This will set new variable with name variable_name with the value of attribute product/attributes.
For more info ... http://www.w3schools.comwww.w3schools.com/xsl/el_choose.asp
EDIT: And another way (a little dirty) by OP's request:
<xsl:variable name="variable_name">
<xsl:for-each select="product/attributes">
<xsl:if test="contains(text(), 'A') or contains(text(), 'B')">
1
</xsl:if>
</xsl:for-each>
</xsl:variable>
It will be helpful if you provide the xml you're writing your xslt against.
This might not help...
Is it 'legal' to have two XML element attributes with the same name (eg. <element x="1" x="2" />)?
Is this what you are trying to process? Try parsing your XML file through xmllint or something like it to see if it is valid.
xmllint --valid the-xml-file.xml
My guess is that you will get a 'attribute redefined' error.
I am rendering a list of tickers to html via xslt and I would like for the list to be comma deliimited. Assuming I was going to use xsl:for-each...
<xsl:for-each select="/Tickers/Ticker">
<xsl:value-of select="TickerSymbol"/>,
</xsl:for-each>
What is the best way to get rid of the trailing comma? Is there something better than xsl:for-each?
<xsl:for-each select="/Tickers/Ticker">
<xsl:if test="position() > 1">, </xsl:if>
<xsl:value-of select="TickerSymbol"/>
</xsl:for-each>
In XSLT 2.0 you could do it (without a for-each) using the string-join function:
<xsl:value-of select="string-join(/Tickers/Ticker, ',')"/>
In XSLT 1.0, another alternative to using xsl:for-each would be to use xsl:apply-templates
<xsl:template match="/">
<!-- Output first element without a preceding comma -->
<xsl:apply-templates select="/Tickers/Ticker[position()=1]" />
<!-- Output subsequent elements with a preceding comma -->
<xsl:apply-templates select="/Tickers/Ticker[position()>1]">
<xsl:with-param name="separator">,</xsl:with-param>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Ticker">
<xsl:param name="separator" />
<xsl:value-of select="$separator" /><xsl:value-of select="TickerSymbol" />
</xsl:template>
I know you said xsl 2.0 is not an option and it has been a long time since the question was asked, but for all those searching for a posibility to do what you wanted to achieve:
There is an easier way in xsl 2.0 or higher
<xsl:value-of separator=", " select="/Tickers/Ticker/TickerSymbol" />
This will read your /Tickers/Ticker elements and insert ', ' as separator where needed
If there is an easier way to do this I am looking forward for advice
Regards Kevin