getSiblings() and sort_by error - sorting

I am working in Liferay with structure (XML) and template (FTL).
My problem is that I do not get how I can use a sort_by() together with getSiblings().
This code does not work, as an example:
<ul id="emedia-categories">
<#list category?sort_by('linktext').getSiblings() as cat>
<li>
<a href="${cat.path.getData()}" title="${cat.title.getData()}">
<h3>
${cat.linktext.getData()}
</h3>
<img src="${cat.image.getData()}" alt="image-alt">
</a>
</li>
</#list>
</ul>
The error I get is the following:
Expected sequence. category evaluated instead to com.liferay.portal.freemarker.LiferayTemplateModel on line 2, column 16 in 14868#14904#131571.
What I want to achieve is to loop over all data and while doing it, I want it to be sorted on the string which is inside each cat.linktext. So the result comes out like: A, B, C, D, E...
Instead of: D, B, E, A, C...
This is my only working variant, but it does not have any sort on linktext, it just loop data in the order it is entered (probably by id):
<ul id="emedia-categories">
<#list category.getSiblings() as cat>
<li>
<a href="${cat.path.getData()}" title="${cat.title.getData()}">
<h3>
${cat.linktext.getData()}
</h3>
<img src="${cat.image.getData()}" alt="image-alt">
</a>
</li>
</#list>
</ul>

The error message is quite clear: You are trying to sort the category, which is not a sequence (= a list or array).
You want to sort the siblings, which is a sequence (= a list), by the attribute linktext.data:
<#list category.siblings?sort_by(['linktext', 'data']) as cat>
...
<#/list>

Related

How do I get the inner html content in this xpath expression?

I have some HTML code
<li><h3>Number Theory - Even Factors</h3>
<p lang="title">Number N = 2<sup>6</sup> * 5<sup>5</sup> * 7<sup>6</sup> * 10<sup>7</sup>; how many factors of N are even numbers?</p>
<ol class="xyz">
<li>1183</li>
<li>1200</li>
<li>1050</li>
<li>840</li>
</ol>
<ul class="exp">
<li class="grey fleft">
<span class="qlabs_tooltip_bottom qlabs_tooltip_style_33" style="cursor:pointer;">
<span>
<strong>Correct Answer</strong>
Choice (A).</br>1183
</span>
Correct answer
</span>
</li>
<li class="primary fleft">
Explanatory Answer
</li>
<li class="grey1 fleft">Factors - Even numbers</li>
<li class="orange flrt">Medium</li>
</ul>
</li>
In the HTML snippet above, I am trying to extract the <p lang="title"> Notice how it has <sup></sup> and <sub></sub> tags being used inside.
My Xpath expression .//p[#lang="title"]/text() does not retrieve the sub and sup contents. How do I get this output below
Desired Output
Number N = 2<sup>6</sup>*5<sup>5</sup> * 7<sup>6</sup> * 10<sup>7</sup>; how many factors of N are even numbers?
XPath
You can simply get innerHTML with node() as below:
//p[#lang="title"]/node()
Note that it returns an array of nodes
Python
You can get required innerHTML with below Python code
from BeautifulSoup import BeautifulSoup
def innerHTML(element):
"Function that receives element and returns its innerHTML"
return element.decode_contents(formatter="html")
html = """<html>
<head>...
<body>...
Your HTML source code
..."""
soup = BeautifulSoup(html)
paragraph = soup.find('p', { "lang" : "title" })
print(innerHTML(paragraph))
Output:
'Number N = 2<sup>6</sup> * 5<sup>5</sup> * 7<sup>6</sup> * 10<sup>7</sup>; how many factors of N are even numbers?'

Wrap lines with tag using different logic in sublime text 2

I have hundreds of list items to code. each list item contains title and description in 2 lines. so what i need to do is wrap 2 lines with a tag. is there any way to do so using sublime text 2? i am using windows OS.
this is the output needed:
<ul>
<li>
this is the title
this is the descrpition
</li>
<li>
this is the title
this is the descrpition
</li>
</ul>
raw text looks like this:
this is title
this is description
this is title
this is description
=====
i have tried using ctrl+shift+G and using ul>li* but unfortunately it wraps each line with <li>
if it is possible with sublime text, i actually need this type of structure:
<ul>
<li>
<span class="title">this is the title</span>
<span class="description">this is the descrpition</span>
</li>
<li>
<span class="title">this is the title</span>
<span class="description">this is the descrpition</span>
</li>
</ul>
How about a two step process using find and replace?
I am assuming that:
your original text is not indented at all;
your indentation is two spaces; and
you will handle the wrap with <ul> and resultant indentation yourself after this is done.
Original state:
this is title
this is description
this is title
this is description
Step one
Ensuring you have enabled regular expression matching do a find and replace using these values.
FIND WHAT :: ((.*\n){1,2})
REPLACE WITH :: <li>\n\1</li>\n
Result:
<li>
this is title
this is description
</li>
<li>
this is title
this is description
</li>
Step two
Ensuring you have enabled regular expression matching do a find and replace using these values.
FIND WHAT :: (<li>\n)(.*)\n(.*)
REPLACE WITH :: \1 <span class="title">\2</span>\n <span class="description">\3</span>
Result:
<li>
<span class="title">this is title</span>
<span class="description">this is description</span>
</li>
<li>
<span class="title">this is title</span>
<span class="description">this is description</span>
</li>
What do you think?
Close enough to be useful?

XPath: Select first element in each row which matches a specific class

Is it possible to select the first element in each row which matches a specific class? This is the HTML structure at the moment.
<ul>
<li>
<article>
<time class="published-date"></time>
<p>Text</p>
</article>
</li>
<li>
<article>
<time class="published-date"></time>
<p>Text</p>
</article>
</li>
<ul>
I was wondering what would be the best and most specific query string in terms of getting the time element with the class published-date in each row?
If there are more time elements with class="published-date" in every row, you need to use indexing (1-based):
//ul/li/article/time[#class = "published-date"][1]
If there is only a single time element in every row, simply do:
//ul/li/article/time[#class = "published-date"]
Using the XPath selector....
//time[#class="published-date"]
...will select all time nodes with the class published-date. XPathFiddle

xpath for locating li with text does not work

Using the xpath //ul//li[contains(text(),"outer")] to find a li in the outer ul does not work
<ul>
<li>
<span> not unique text, </span>
<span> not unique text, </span>
outer ul li 1
<ul >
<li> inner ul li 1 </li>
<li> inner ul li 2 </li>
</ul>
</li>
<li>
<span> not unique text, </span>
<span> not unique text, </span>
outer ul li 2
<ul >
<li> inner ul li 1 </li>
<li> inner ul li 2 </li>
</ul>
</li>
</ul>
Any idea how to find a li with a specific text in the outer ul?
Thank you
This will work for you //ul//li[contains(.,"outer")]
I would expect that you only like to consider the text nodes which are direct child of the li. Therefore you are right with using text() (if you use contains(.,"outer") this will consider text form any children of li).
Therefore try this:
//ul/li[text()[contains(.,'outer')]]
Running this with Saxon, the original XPath expression gives:
XPTY0004: A sequence of more than one item is not allowed as the first argument of
contains() ("", "", ...)
Now, I guess Selenium is probably using XPath 1.0 rather than XPath 2.0, and in 1.0 the contains() function has "first item semantics" - it converts its argument to a string, which if the argument is a node-set containing more than one node, involves considering only the first node. And the first text node is probably whitespace.
If you want to test whether some child text node contains "outer", use
//ul//li[text()[contains(.,"outer")]]
Another reason for switching to XPath 2.0...
For above issue -
This solution will work
//ul//li[contains(.,"outer")]
"." Selects the current node

XPath and negation searches

I have the following code sample in an xmlns root:
<ol class="stan">
<li>Item one.</li>
<li>
<p>Paragraph one.</p>
<p>Paragraph two.</p>
</li>
<li>
<pre>Preformated one.</pre>
<p>Paragraph one.</p>
</li>
</ol>
I would like to perform a different operation on the first item in <li> depending on the type of tag it resides in, or no tag, i.e. the first <li> in the sample.
EDIT:
My logic in pursuing the task turns out to be incorrect.
How do I query a <li> that has no descendants as in the first list item?
I tried negation:
#doc.xpath("//xmlns:ol[#class='stan']//xmlns:li/xmlns:*[1][not(p|pre)]")
That gives me the exact opposite for what I think I am asking for.
I think I am making the expression more complicated since I can't find the right solution.
UPDATE:
Navin Rawat has answered this one in the comments. The correct code would be:
#doc.xpath("//xmlns:ol[#class='stan']/xmlns:li[not(xmlns:*)]")
CORRECTION:
The correct question involves both an XPath search and a Nokogiri method.
Given the above xhtml code, how do I search for first descendant using xpath? And how do I use xpath in a conditional statement, e.g.:
#doc.xpath("//xmlns:ol[#class='stan']/xmlns:li").each do |e|
if e.xpath("e has no descendants")
perform task
elsif e.xpath("e first descendant is <p>")
perform second task
elsif e.xpath("e first descendant is <pre>")
perform third task
end
end
I am not asking for complete code. Just the part in parenthesis in the above Nokogiri code.
Pure XPath answer...
If you have the following XML :
<ol class="stan">
<li>Item one.</li>
<li>
<p>Paragraph one.</p>
<p>Paragraph two.</p>
</li>
<li>
<pre>Preformated one.</pre>
<p>Paragraph one.</p>
</li>
</ol>
And want to select <li> that has no child element as in the first list item, use :
//ol/li[count(*)=0]
If you have namespaces problem, please give to whole XML (with the root element and namespaces declaration) so that we can help you dealing with it.
EDIT after our discussion, here is your final tested code :):
#doc.xpath("//xmlns:ol[#class='footnotes']/xmlns:li").each do |e|
if e.xpath("count(*)=0")
puts "No children"
elsif e.xpath("count(*[1]/self::xmlns:p)=1")
puts "First child is <p>"
elsif e.xpath("count(*[1]/self::xmlns:pre)=1")
puts "First child is <pre>"
end
end

Resources