XSLT contains two periods
I have an HTML document that used an unordered list to display the navigation. When it is output as XML it no longer uses the list, but instead assigns each link a <Description>
. The way that the description works is that the main links are assigned numbers like 100, 200, 300, 400, and so on. Subnavigation links from those are assigned as follows: 200.100, 200.200, 200.300, and so on.
The XML looks like the following:
<Items>
<PgIndexElementItem>
<Description>100</Description&g开发者_如何转开发t;
<Title>This is the Title</Title>
</PgIndexElementItem>
<PgIndexElementItem>
<Description>200</Description>
<Title>This is the Title</Title>
</PgIndexElementItem>
<PgIndexElementItem>
<Description>200.100</Description>
<Title>This is the Title</Title>
</PgIndexElementItem>
<PgIndexElementItem>
<Description>200.100.100</Description>
<Title>This is the Title</Title>
</PgIndexElementItem>
</Items>
If you look at the last PgIndexElementItem, there are three sets of numbers.
I'm trying to recreate the unordered list using XSLT. This is what I have been doing so far:
<ul>
<xsl:for-each select="//PgIndexElementItem">
<xsl:if test="not(contains(Description, '.'))">
<li><a href="{ResolvedURL/ServerRelative}"><xsl:value-of select="Title"/></a>
<ul>
<xsl:for-each select="//PgIndexElementItem">
<xsl:if test="contains(Description, '.')">
<li><a href="{ResolvedURL/ServerRelative}"><xsl:value-of select="Title"/></a></li>
</xsl:if>
</xsl:for-each>
</ul>
</li>
</xsl:if>
</xsl:for-each>
</ul>
My question would be, is there a way to specify a contains(Description
that targets descriptions with two periods, like the last PgIndexElementItem above?
I hope I explained this well enough. I don't know how to make it less confusing. Thanks in advance!
EDIT
o Graduate Program
* How to Apply
* Masters Program
o M.A. Handbook
o FAQs
o Alumni
o Masters Theses
o Sample Thesis Proposals
* M.A. Handbook
* FAQs
* Alumni
* Masters Theses
* Sample Thesis Proposals
You can use
<xsl:if test="contains(substring-after(Description, '.'), '.')">
...
to test whether an element has a Description child with two or more periods.
However, I would instead change your XSLT to:
<!-- this part replaces your <ul> code above -->
<ul>
<xsl:apply-templates
select="//PgIndexElementItem[not(contains(Description, '.'))]" />
</ul>
Updated this template:
<!-- add this new template -->
<xsl:template match="PgIndexElementItem">
<li>
<a href="{ResolvedURL/ServerRelative}"><xsl:value-of select="Title"/></a>
<xsl:variable name="prefix" select="concat(Description, '.')"/>
<xsl:variable name="childOptions"
select="../PgIndexElementItem[starts-with(Description, $prefix)
and not(contains(substring-after(Description, $prefix), '.'))]"/>
<xsl:if test="$childOptions">
<ul>
<xsl:apply-templates select="$childOptions" />
</ul>
</xsl:if>
</li>
</xsl:template>
This will avoid unnecessary code duplication, and unwanted empty <ul>
elements.
I've made some assumptions about how the Description children are to be interpreted... e.g. "200.100" should appear in a sublist under "200" but not under "100".
Use:
string-length(Description) - string-length(translate(Description, '.','')) = 2
This works the same for any number $n
(while the "substring-after" technique is simply unusable in the general case):
string-length(Description) - string-length(translate(Description, '.','')) = $n
I looks like you're dealing with nested PgIndexElementItem
elements - if they nest in multiple layers you should consider refactoring your XSL to use a recursive template (assuming the HTML output for each level is predictable).
As it is written, your for-each loop selects all the PgIndexElementItem descendants of the current element, then does that again when it hits the inner for-each loop. Possibly not what you intended.
精彩评论