This is my sample input
<table id="1" style="width=100%">
<tr>
<td id="1">
<table id="2" style="width=50%">
<tr>
<td id="2">
</td>
</tr>
</table>
</td>
</tr>
</table>
I am using xslt1.0. when ever the template match 'td' is matched, i need to find the corresponding table id value..For example, If the td with id=1 is matched,i want to take style attribute value from table(id=1) and if the td with id=2 is matched,i want to take style attribute value from table(id=2). I have written ancestor::table/#style in my template, but both td are referring the styles of table with id=1.
I have written
ancestor::table/#style in my
template
You were close. Because there can be more than one table in the ancestor axis, you need to get the first one like in ancestor::table[1]/#style. Of course, if you are absolute sure there is always a chain of table -> tr -> td (not optional tbody) then you could go with #Flack's answer.
Assuming you are in 'td' context, use this XPath:
../../#style
Test XSLT against your sample:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="td">
<xsl:value-of select="../../#style"/>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Result:
width=100%width=50%
Try this XPath it works perfectly
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="td">
<xsl:value-of select="ancestor::table[1]/#style"/>
<xsl:apply-templates/>
</xsl:template>
Result:
width=100%width=50%
Related
I've just started learning XML/XSL and I've hit a roadblock in one of my assignments. Tried Googling and searching over here but I can't seem to find a question that has a solution that is basic. so what I am trying is to display rows of bucket-type and room-types associated with it. can somebody please help
<list-inventory list-count="2">
<list list-type="Standard" list-order = "1" count-Types = "3">
<types type="BEN2D"></room>
<types type="BESH2D"></room>
<types type="HNK"></room>
</list>
<list list-type="Deluxe" list-order = "2" count-Types = "3">
<types type="SNK"></room>
<types type="TESTKD"></room>
<types type="TESTKD"></room>
</list>
<list-inventory>
I want table as below
Standard | Deluxe
BEN2D |SNK
BESH2D |TESTKD
HNK |TESTKD
I tried below xsl code but i see all list-type in single column and only 1st is being printing for all list-type:
<xsl:for-each select="/contents/list-inventory/list">
<tr>
<td class="alt-th" style="border:1px solid black">
<xsl:value-of select="#list-type"/>
</td>
</tr>
<tr>
<td style="border:1px solid black">
<xsl:for-each select="/contents/list-inventory/list/types">
<span><xsl:value-of select="#type"/></span>
<xsl:if test="position()!=last()">
<br/>
</xsl:if>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
Can someone help me with xsl:for-each inside a xsl:for-each
It's too bad you did not post your expected result as code. I would assume that you want a separate row for each pair of values. As I stated in the comments, this is far from being trivial.
However, you could make it simpler if you are willing to settle for a single data row, where each cell contains all the values of the corresponding list (your attempt seems to suggest that this is what you actually tried to accomplish).
So, given a well-formed XML input:
XML
<list-inventory list-count="2">
<list list-type="Standard" list-order = "1" count-Types = "3">
<types type="BEN2D"/>
<types type="BESH2D"/>
<types type="HNK"/>
</list>
<list list-type="Deluxe" list-order = "2" count-Types = "3">
<types type="SNK"/>
<types type="TESTKD"/>
<types type="TESTKD"/>
</list>
</list-inventory>
you could do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/list-inventory">
<table border="1">
<!-- header -->
<tr>
<xsl:for-each select="list">
<th>
<xsl:value-of select="#list-type"/>
</th>
</xsl:for-each>
</tr>
<!-- body -->
<tr>
<xsl:for-each select="list">
<td>
<xsl:for-each select="types">
<xsl:value-of select="#type"/>
<br/>
</xsl:for-each>
</td>
</xsl:for-each>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
to get:
Result
<?xml version="1.0" encoding="UTF-8"?>
<table border="1">
<tr>
<th>Standard</th>
<th>Deluxe</th>
</tr>
<tr>
<td>BEN2D<br/>BESH2D<br/>HNK<br/>
</td>
<td>SNK<br/>TESTKD<br/>TESTKD<br/>
</td>
</tr>
</table>
which would render as:
I'm not sure how general you want your solution to be (i.e what inputs does it have to handle other than the example shown), but I would do something like:
<xsl:template match="list-inventory">
<xsl:variable name="list2" select="list[2]/types"/>
<xsl:for-each select="list[1]/types">
<xsl:variable name="position" select="position()"/>
<tr>
<td><xsl:value-of select="#type"/></td>
<td><xsl:value-of select="$list2[$position]/#type"/></td>
</tr>
</xsl:for-each>
</xsl:template>
Both the variables here are needed to avoid problems with context: the effect of an XPath expression depends on the context at the time it is evaluated, so you can evaluate a variable to capture information at the time you're in the right context.
Is is possible to get all the xPaths used in an XSLT file?
For example:
XSLT File:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="/catalog/cd">
<tr>
<td>
<xsl:value-of select="title"/>
</td>
<td>
<xsl:value-of select="artist"/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
As output I want a list like this:
/catalog/cd
/catalog/cd/title
/catalog/cd/artist
Maybe by making a custom post processor that outputs a line everytime an xPaths has matched/not been matched?
Ideas are welcome, because I'm very hopeless :)
Thanks!
You can use this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="*">
<xsl:for-each select="ancestor-or-self::*">
<xsl:text>/</xsl:text>
<xsl:value-of select="local-name(.)"/>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
It's difficult because template rule matching in XSLT is very dynamic: if you have a template with match="cities" that calls xsl:apply-templates, and if you have another template with match="city", then the XSLT compiler can't know that there is a path /cities/city.
Internally Saxon has the capability to build a "path map" rather as you describe, and it's used to implement "document projection" in XQuery, but it's of very little use in XSLT because of dynamic template rule despatch.
So static analysis to determine the paths isn't going to get you very far. You also suggest that dynamic analysis - capturing the paths visited at run-time - might also be of interest. In principle you can do that in Saxon with a TraceListener. The difficulty is in determining exactly what you mean by "visited" - for example do you consider xsl:copy-of select="/" as visiting every node in the document, or only the root node?
I think it might be a silly question, but I read the documentation but it still not working for me.
I have this graphxml (generated my mvn):
<?xml version="1.0" encoding="UTF-8"?> <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key for="node" id="d0" yfiles.type="nodegraphics"/>
<key for="edge" id="d1" yfiles.type="edgegraphics"/>
<graph id="dependencies" edgedefault="directed">
<node id="966567431"><data key="d0"><y:ShapeNode><y:NodeLabel>myproject.mulesoft.services:utilitymgmt-services:mule:3.0.0-SNAPSHOT</y:NodeLabel></y:ShapeNode></data></node>
<node id="706960270"><data key="d0"><y:ShapeNode><y:NodeLabel>myproject.mulesoft.context:custom-runtime-context:jar:1.0.0-SNAPSHOT:compile</y:NodeLabel></y:ShapeNode></data></node>
<edge source="966567431" target="706960270"><data key="d1"><y:PolyLineEdge><y:EdgeLabel>compile</y:EdgeLabel></y:PolyLineEdge></data></edge>
<node id="1985178707"><data key="d0"><y:ShapeNode><y:NodeLabel>myproject.mulesoft.library:common-error-library:jar:2.0.0-SNAPSHOT:compile</y:NodeLabel></y:ShapeNode></data></node>
<node id="953191605"><data key="d0"><y:ShapeNode><y:NodeLabel>myproject.mulesoft.notification:utility-common-domains:jar:3.0.0-SNAPSHOT:compile</y:NodeLabel></y:ShapeNode></data></node>
<edge source="1985178707" target="953191605"><data key="d1"><y:PolyLineEdge><y:EdgeLabel>compile</y:EdgeLabel></y:PolyLineEdge></data></edge>
<edge source="966567431" target="1985178707"><data key="d1"><y:PolyLineEdge><y:EdgeLabel>compile</y:EdgeLabel></y:PolyLineEdge></data></edge>
</graph></graphml>
And all I'm trying to do at this point is to generate a HTML that shows a table with the dependencies like:
Dependencies
myproject.mulesoft.services:utilitymgmt-services:mule:3.0.0-SNAPSHOT
myproject.mulesoft.context:custom-runtime-context:jar:1.0.0-SNAPSHOT:compile
compile
myproject.mulesoft.library:common-error-library:jar:2.0.0-SNAPSHOT:compile
myproject.mulesoft.notification:utility-common-domains:jar:3.0.0-SNAPSHOT:compile
compile
compile
So this is the xsl I have:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Dependency</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Items</th>
</tr>
<xsl:for-each select="*">
<tr>
<td><xsl:value-of select="/"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
And I was expecting to have each item of the list into a separate TR TD.
But instead it's all into the same TR TD.
Items
com.quadreal.mulesoft.services:qr-identitymgmt-services:mule:3.0.0-SNAPSHOT com.quadreal.mulesoft.context:quadreal-runtime-context:jar:1.0.0-SNAPSHOT:compile compile com.quadreal.mulesoft.library:qr-common-error-library:jar:2.0.0-SNAPSHOT:compile com.quadreal.mulesoft.notification:quadreal-utility-common-domains:jar:3.0.0-SNAPSHOT:compile compile compile
Also even if I remove the for-each tag and keep only the
It still displaying the whole thing instead displaying only the first element.
Also tried to add a template for graph and for-each the elemtns, but then I don't even get the html. I get only the whole text for the dependencies.
Am I missing something or there is something with the graphml that is not properly generated?
I'm adding here the expected HTML code:
<html>
<body>
<h2>Dependency</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Items</th>
</tr>
<tr>
<td>myproject.mulesoft.services:utilitymgmt-services:mule:3.0.0-SNAPSHOT
</td>
</tr>
<tr>
<td>myproject.mulesoft.context:custom-runtime-context:jar:1.0.0-SNAPSHOT:compile
</td>
</tr>
<tr>
<td>compile</td>
</tr>
<tr>
<td>myproject.mulesoft.library:common-error-library:jar:2.0.0-SNAPSHOT:compile
</td>
</tr>
<tr>
<td>myproject.mulesoft.notification:utility-common-domains:jar:3.0.0-SNAPSHOT:compile
</td>
</tr>
<tr>
<td>compile</td>
</tr>
<tr>
<td>compile</td>
</tr>
</table>
</body>
</html>
You are getting only one table row, because you do:
<xsl:for-each select="*">
from the context of the / root node, established by:
<xsl:template match="/">
The / root node has only one child, and that is the graphml root element.
Perhaps you wanted to do:
<xsl:for-each select="*/*/*">
to create a row for every node and/or edge?
Note also that:
<xsl:value-of select="/"/>
makes no sense. It will return the entire text of the entire document. If you'll change it to:
<xsl:value-of select="."/>
you will get the expected result - at least in the given example.
I come across this small problem while creating a xslt file... I have this generic xml file:
<data>
<folder>
<file>
<name>file1</name>
<date>2000</date>
<index1>1</index1>
<index2>1</index2>
</file>
<file>
<name>file2</name>
<date>2001</date>
<index1>1</index1>
<index2>1</index2>
</file>
<file>
<name>file3</name>
<date>2004</date>
<index1>2</index1>
<index2>1</index2>
</file>
</folder>
</data>
Given this abstract example, I have to transform it into something like:
<table>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
<tr>
<td>file1</td>
<td>2000</td>
</tr>
<tr>
<td>file2</td>
<td>2001</td>
</tr>
</table>
<table>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
<tr>
<td>file3</td>
<td>2004</td>
</tr>
</table>
I have to group the file elements per table based on their index1 and index2 (like an ID pair). I am able to create a table for every separated file, but I can't come with a solution to create a table for every file sharing index1 and index2. Any idea or suggestion?
Since you are using XSLT 2.0 you can use the xsl:for-each-group statement. You have two choices here, depending on whether you wish to keep groups together and respect the sequence or whether you just want to group regardless of sequence.
That is, given aabaab would you want groups of (aaaa, bb) or (aa, b, aa, b)?
This first groups all file elements with the same index1 and index2 regardless of order in the document (I've put in the body element just to make it well-formed)
<xsl:template match="folder">
<body>
<xsl:for-each-group select="file" group-by="concat(index1, '-', index2)">
<!-- xsl:for-each-group sets the first element in the group as the context node -->
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</body>
</xsl:template>
<xsl:template match="file">
<table>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
<xsl:apply-templates select="current-group()" mode="to-row"/>
</table>
</xsl:template>
<xsl:template match="file" mode="to-row">
<tr>
<xsl:apply-templates select="name|date"/>
</tr>
</xsl:template>
<xsl:template match="name|date">
<td><xsl:apply-templates/></td>
</xsl:template>
The second version would only need the first template changed to:
<xsl:template match="folder">
<body>
<xsl:for-each-group select="file" group-adjacent="concat(index1, '-', index2)">
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</body>
</xsl:template>
This is for a category index of blog posts-I only want to show the category once using xsl v1.0. There will be multiple posts in each category. The desired result is:
Cat Name 1
cat Name 2
Cat Name 3
I assume grouping the items and only showing the first one in a group (using the cat name as a key) would work but the Muenchian method is a bit beyond my abilities. So a simpler method or a simple explanation of the Muenchian method would be most appreciated.
The xml
<Root>
<Schema>
<Field Type="Lookup" DisplayName="Category name" Required="FALSE" ShowField="Category_x0020_name" Name="Category_x0020_name" Group="" />
<Field ReadOnly="TRUE" Type="Computed" Name="LinkTitle" DisplayName="Post number" />
</Schema>
<Data ItemCount="1">
<Row Category_x0020_name="" LinkTitle="" />
</Data>
</Root>
The xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="/">
<table border="0" cellpadding="0" cellspacing="0">
<h3>Categories</h3>
<xsl:for-each select="//Data/Row">
<xsl:if test="./#Category_x0020_name !=''">
<tr valign="top"> <td>
<a href="/cat{./#LinkTitle}.aspx">
<xsl:value-of select="./#Category_x0020_name" /></a></td> </tr>
</xsl:if>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Do not be afraid of Meunchian method. Use it once, and you will be able to apply it whenever you need it.
collect the wanted data to group in a key
apply the templates to only one node with same key using a predicate like
generate-id()=generate-id(key(...)[1])
That's what you need know to use Meunchian grouping. Here to get you started:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="Cat" match="Data/Row" use="#Category_x0020_name"/>
<xsl:template match="/*/Data">
<xsl:apply-templates select="Row
[generate-id()
= generate-id(key('Cat',#Category_x0020_name)[1])]"/>
</xsl:template>
<xsl:template match="Row">
<xsl:value-of select="concat(#Category_x0020_name,'
')"/>
</xsl:template>
</xsl:stylesheet>
Since you don't need to list the members of each category, and assuming your data sets are not very large so performance is not a big factor, you can forego Muenchian grouping for something a little less elegant. Just change your <xsl:if test> to:
<xsl:if test="./#Category_x0020_name !='' and
not(./#Category_x0020_name = preceding::Row/#Category_x0020_name)">
In other words, only output a category name the first time it occurs.
Incidentally, you can remove the ./ wherever it occurs at the beginning of an XPath expression. It's redundant. It means "starting from the context node," but you're already starting from the context node. If you want to leave it in for readability or something, that's OK.
Then with input like
<Root>...
<Data ItemCount="1">
<Row Category_x0020_name="foo" LinkTitle="Foo" />
<Row Category_x0020_name="bar" LinkTitle="Bar" />
<Row Category_x0020_name="foo" LinkTitle="Foo" />
</Data>
</Root>
You get this output:
<table border="0" cellpadding="0" cellspacing="0">
<h3>Categories</h3>
<tr valign="top">
<td>foo</td>
</tr>
<tr valign="top">
<td>bar</td>
</tr>
</table>