xpath expression dependent upon siblings - xslt

<a id ="1">
...<c>
......<b/>
......<f/>
......<b/>
......<f/>
...</c>
</a>
<a id="2">
...<c>
......<b/>
......<f/>
......<f/>
...</c>
</a>
If any elem b is followed by two or more f elements in order, return node a. I prefer straight XPath 2.0 solution, if possible. What xpath will get me a2 but not a1? I have tried following siblings, position, and such, to no avail.

With XPath 1.0:
a[.//b[following-sibling::*[1]/self::f and following-sibling::*[2]/self::f]]

You could do:
//a[.//b/following-sibling::*[1][self::f]/following-sibling::*[1][self::f]]
This says to find the a element that contains a b element, which is immediately followed by a f element, which is immediately followed by a f element.

Something like below will work using XPATH 1.0:
//b[following-sibling::*[
position()=1 and self::f
and
./following-sibling::*[
position()= 1 and self::f
]
]
]/ancestor::a[1]
output
<a id="2">
...<c>
......<b/>
......<f/>
......<f/>
...</c>
</a>

you can get a2 using this :
//a[#id=2]
just by using the id attribute.

I came up with this:
/a//c[f/following-sibling::*[1] = f]

Related

How can I use the concat() function on multiple children within an XPath expression

something like this works fine "concat('a', 'b')" but i can't it to work as part of an existing expression, i.e.
<div class='xyz'>
<strong>the</strong>
<sub> one</sub>
</div>
//div[class='xyz']/concat(/strong/text(), /sub/text())
i'd like it to return 'the one'
i'd like it to return 'the one'
In XPath 1.0, you'd have to do:
concat(div[#class='xyz']/strong, div[#class='xyz']/sub)
In XSLT 1.0, you could do:
<xsl:template match="/">
<xsl:for-each select="div[#class='xyz']/*">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
I hope this is XPath 2.0? Otherwise, concat() cannot appear on the rh-side of the / (or either side of |) in an expression, because it returns an atomic value, not a node. This limitation was removed in XPath 2.0 and up.
XPath 2.0
Your expression is almost correct, the difference being the root node. You started your concat-expressions with /strong and /sub, but these elements are not rooted at the document root. The rh-side of the expression takes the focus of the lh-side of the expression, that is, its focus is on div:
//div[#class='xyz']/concat(strong/text(), sub/text())
In addition (thanks to Rubens), you want the attribute class, not the child element class.
If your elements do not contain mixed content and you are interested on all text data inside it, you can simplify as follows:
//div[#class='xyz']/concat(strong, sub)
XPath 1.0
If you are stuck with XPath 1.0, you can do the following, but it is not (quite) the same:
concat(//div[#class='xyz']/strong, //div[#class='xyz']/sub)
If you want to apply this to all divs (as indicated by //) you should resort to XSLT instead.

How to write a schematron to ensure list-items are alphanumeric?

Is it possible to use schematron to ensure that the list items are in alphanumeric order?
<ul>
<li>1</li>
<li>a</li>
<li>d</li>
<li>g</li>
</ul>
Many thanks!
Yes, it is possible. You can use something like this example rule that reports all <li> elements whose value is lower than (lt) their previous <li> sibling value.
<sch:rule context="li">
<sch:report test=". lt preceding-sibling::li[1]">
This li value is lower than his previous li sibling value.
</sch:report>
</sch:rule>

pugixml: selecting nodes fails

I'm using pugixml to parse the following xml:
<td class="title">
<div class="random" />
Link1
</td>
<td class="title">
<div class="random" />
Link2
</td>
etc...
I want the value of every 'a href' in a td class ="title" (which appears an indeterminate number of times) but only the first such instance.
I am using the following code to try and get these values:
pugi::xpath_node_set link_nodes = list_doc.select_nodes("//td[#class='title']");
for (pugi::xpath_node_set::const_iterator it = link_nodes.begin();it != link_nodes.end();++it)
{
pugi::xpath_node single_link_node = *it;
std::cout << single_link_node.node().select_single_node("//a").node().attribute("href").value()<<std::endl;
}
which doesn't seem to work (it outputs number of times but with a value that doesn't even seem to appear within that element).
Thanks.
"//a" selects all "a" nodes in the document; you probably meant ".//a" that selects all "a" nodes in the subtree.
You can also use one XPath expression instead of multiple:
//td[#class='title']//a[1]
This selects the first tag for each td - i.e. [1] only applies to //a, not to the full expression.

XSL: Calling specific Element from Multiple Instances of one Schema

I created a component named "BusLocationLinks" it stores the name of the business as well as the coordinates for a map I created.
I have nearly 50 business locations with that same schema (BusLocationsLinks) and only want to list the element "Business Name" of all components components of that name. I've tried everything but can not make them all display. ANY ADVICE?
Here's my current code:
<xsl:template name="BusLocationLinks">
<xsl:for-each select="BusLocationLinks/BusinessName">
<li class="active">
<xsl:value-of select="BusinessName" />
</li>
</xsl:for-each>
</xsl:template>
my xml code looks similar to such:
<BusLocationLinks>
<BusinessName>Star Property</BusinessName>
</BusLocationLinks>
Without seeing your XML it is difficult to diagnose the problem. However, it is likely that you have the following structure:
<BusLocationLinks>
<BusinessName>name1</BusinessName>
<BusinessName>name2</BusinessName>
<BusinessName>name3</BusinessName>
</BusLocationLinks>
If that is the case, then you should adjust your XSLT like this:
<xsl:template name="BusLocationLinks">
<xsl:for-each select="BusinessName">
<li class="active">
<xsl:value-of select="." />
</li>
</xsl:for-each>
</xsl:template>
The body of an xsl:for-each instruction resets the context node to be one of the nodes in the selected node set (a different one each time the body of the for-each is evaluated).
In your example, that means that within the body of the for-each, the current node is one of the BusLocationLinks/BusinessName elements you selected. Your loop creates a list-item element for each of them (check your output, I expect you'll see them there) containing the value of the BusinessName child of the context node. The context node matches the expression BusLocationLinks/BusinessName, so you are looking for the values of nodes which match BusLocationLinks / BusinessName / BusinessName. If you don't have any nodes that match the expression BusLocationLinks / BusinessName / BusinessName, you'll be getting empty li elements.
Try <xsl:value-of select="."/>.

a bit foggy about the xpath 'following' axis (as used in xslt)

<a>
<x/>
<m/>
<y/>
<m/>
</a>
Inside a template that matches 'a', I want to match (first) any 'm's before 'y', and then
separately any 'm's after 'y'.
<xsl:apply-templates select="./m[following::y]"/>
is what I thought of, but I can't get it to work, and, further, I can't see how to prevent the template that matches on 'm' from being applied in the normal flow as well as in the particular place i want to insert the m-related content.
Your template looks OK, but are you sure you want to use following? For example, this template:
<xsl:template match="a">
<a><xsl:apply-templates select="m[following::y]"/></a>
<b><xsl:apply-templates select="m[following-sibling::y]"/></b>
</xsl:template>
...applied to the following XML:
<a>
<x/>
<m>match</m>
<y/>
<m>no match</m>
<nested>
<m>match 2</m>
<y/>
</nested>
</a>
...outputs the following result:
<a>matchno match</a>
<b>match</b>
The first apply-templates matches <m>no match</m> because following includes all nodes that are after the context node in document order, which includes the nested <y/>.
The second template matches only siblings.
For completeness, I'll add the following template, which matches only those <m> nodes whose immediate following sibling is a <y>:
<xsl:template match="a">
<a><xsl:apply-templates select="m[following-sibling::*[1][self::y]]"/></a>
</xsl:template>
This template outputs the following, given the above XML:
<a>match</a>