I am looking for ways to grab an element by name. I tried iterating one element at a time by using Element.getAttributes.getAttributeNames() and iterated through each element to find the name then checked it with the name I am looking for. Any alternatives or optimized way to grab the element directly?
This is the method I use to retrieve elements by tag name. For mixed-content elements (e.g., sub, sup, b, i), you need to look in the attributes of the “fake” content elements.
/**
* Returns all elements of a particular tag name.
*
* #param tagName The tag name of the elements to return (e.g., HTML.Tag.DIV).
* #param document The HTML document to find tags in.
* #return The set of all elements in the HTML document having the specified tag name.
*/
public static Element[] getElementsByTagName(HTML.Tag tagName, HTMLDocument document)
{
List<Element> elements = new ArrayList<Element>();
for (ElementIterator iterator = new ElementIterator(document); iterator.next() != null;)
{
Element currentEl = iterator.current();
AttributeSet attributes = currentEl.getAttributes();
HTML.Tag currentTagName = (HTML.Tag) attributes.getAttribute(StyleConstants.NameAttribute);
if (currentTagName == tagName)
{
elements.add(iterator.current());
} else if (currentTagName == HTML.Tag.CONTENT) {
for (Enumeration<?> e = attributes.getAttributeNames(); e.hasMoreElements();)
{
if (tagName == e.nextElement())
{
elements.add(iterator.current());
break;
}
}
}
}
return elements.toArray(new Element[0]);
}
I am looking for ways to grab an element by name.
Perhaps you could use the getElement() method? It takes as an argument the String value for the id of the element you are searching for.
Related
I want to select all the elements on the page containing any text.
Only elements actually containing texts themselves, not the parent elements containing texts in their child elements only.
This XPath is matching elements containing any non-empty texts
//*[text() != ""]
However this
List<WebElement> list = driver.findElements(By.xpath("//*[text() != '']"));
gives me a list of all elements containing texts themselves or in their child elements.
I can iterate over this list with something like this to get elements actually containing texts themselves into real list
List<WebElement> real = new ArrayList<>();
for(WebElement element : list){
js = (JavascriptExecutor)driver;
String text = js.executeScript("""
return jQuery(arguments[0]).contents().filter(function() {
return this.nodeType == Node.TEXT_NODE;
}).text();
""", element);
if(text.length()>0){
real.add(element);
}
But this is a kind of workaround.
I'm wondering is there a way to get the list of elements actually containing any text doing that directly or more elegantly?
List<WebElement> elementsWithOwnText = new ArrayList<WebElement>();
List<WebElement> allElements = driver.findElements(By.xpath("//*"));
for (WebElement element: allElements) {
List<WebElement> childElements = element.findElements(By.xpath(".//*"));
String text = element.getText();
if (childElements.size() == 0 && text.lenght() > 0) {
elementsWithOwnText.add(element);
}
}
Be aware of org.openqa.selenium.StaleElementReferenceException. While looping allElements any of them may be no more attached to the page document (dynamic content f.e.).
You can try this:
it selects all leaf elements with text.
List<WebElement> list = driver.findElements(By.xpath("//*[not(child::*) and text()]"));
for (WebElement webElement : list)
System.out.println(webElement.getText());
Until you find the xpath that you need, as a temporary solution, I would recommand to try the below iteration too (even though is not so efficient as a direct xpath).
In my case it took 1 minute to evaluate 700 nodes with text and returned 152 elements that have its own text:
public static List<WebElement> getElementsWithText(WebDriver driver) {
return driver.findElements(By.xpath("//*[normalize-space() != '']"))
.stream().filter(element -> doesParentHaveText(element))
.collect(Collectors.toList());
}
private static boolean doesParentHaveText(WebElement element) {
try {
String text = element.getText().trim();
List<WebElement> children = element.findElements(By.xpath("./*"));
for (WebElement child: children) {
text = text.replace(child.getText(), "").trim();
}
return text.trim().replace("[\\n|\\t|\\r]", "").length() > 0;
} catch (WebDriverException e) {
return false; //in case something does wrong on reading text; you can change the return false with thrown error
}
}
this could help:
source
List<String> elements = driver.findElements(By.xpath("//a")).stream().map(productWebElement -> productWebElement.getText()).distinct().collect(Collectors.toList());
// Print count of product found
System.out.println("Total unique product found : " + elements.size());
// Printing product names
System.out.println("All product names are : ");
elements.forEach(name -> System.out.println(name));
I need to create a method that given any LinkedHashMap , this method have to transform it into XML/Dom elements of a Document like :
#Override
public Element marshal(Object linkedHashMap) {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
final Element element = doc.createElement("root");
//For each attribute and object of the linked hashmap
// I must iterate recursively in order to get all the objects and atributes of the LinkedHashMap and append them to the root node element:
//element.appendChild(...));
return element; // here the element must be already populated with all the attributes of the linked hashmap and its values.
}
I have no idea about how can I achieve this , how can I loop through the attributes of a LinkedHashMap in order to map them to Element ?
I need something like this , but it must iterate over all the levels and sublevels (nested linkedhashmap objects) of the linkedhashmap:
private void marshalMapElements(ArrayList<LinkedHashMap> linkedHashMaps) {
Document doc = getDocument();
Element root = doc.createElement("root");
for (Map<String, Object> element : linkedHashMaps) {
Element e = doc.createElement(element.getKey());
e.setTextContent(element.getValue());
root.appendChild(e);
}
}
}
I am currently parsing an XML response from a web service. It returns a finite number of <result> elements. I am currently iterating through a NodeList of such results.
While I iterate through this, sometimes I need to find the value of an attribute that exists in each <result> element. In that case, I want to call a method that traverses through all of the child nodes (and potentially the children's children, etc.) and returns the attribute's value.
I have attempted to do this recursively:
private String findAttrInChildren(Element element, String tag) {
if (!element.getAttribute(tag).isEmpty()) {
return element.getAttribute(tag);
}
NodeList children = element.getChildNodes();
for (int i = 0, len = children.getLength(); i < len; i++) {
if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) children.item(i);
return findAttrInChildren(childElement, tag);
}
}
// We didn't find it, return null
return null;
}
Unfortunately, this isn't working. Is recursion the best approach here? I think the fact that I want to return a value at the end is messing me up somewhere along the line, rather than implementing a void recursive method.
You leave the recursion too early. Given
if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) children.item(i);
return findAttrInChildren(childElement, tag);
}
this will end the recursive search at the first child element - regardless if the child or one of its descendants has the attribute or not.
So test if the returned attribute is not null:
if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) children.item(i);
String attr = findAttrInChildren(childElement, tag);
if (attr != null)
return attr;
}
I know your question was answered and you got an answer that works but you can actually make your code a little better. You should avoid 2 exit points and have 1 instead and always avoid null. So to cater these both I am going to modify your code and provide you a new one. I hope you understand the changes. I am using Optional to avoid null and make your code a little more readable and understandable.
private static Optional<String> findAttributeInChildren(Element element, String tag) {
Optional<String> attr = Optional.empty();
if (!element.getAttribute(tag).isEmpty()) {
attr = Optional.of(element.getAttribute(tag));
} else {
NodeList children = element.getChildNodes();
int len = children.getLength();
for (int i = 0; (i < len) && (!attr.isPresent()); i++) {
if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) children.item(i);
attr = findAttributeInChildren(childElement, tag);
}
}
}
return attr;
}
And if you want to check whether your code has some value or not you just do;
findAttributeInChildren(x,y).isPresent()
rather than
findAttributeInChildren(x,y) == null
hello guys i am try to compare one jsoup element with all other elements and if two elements are equal i need to make count++; in this case i need to compare all elements in links1 with all elements in links2 links3 links4....
Document document1 = Jsoup.parse(webPage1);
Elements links1 = document1.select("example");
Document document2 = Jsoup.parse(webPage2);
Elements links2 = document2.select("example");
Document document3 = Jsoup.parse(webPage3);
Elements links3 = document3.select("example");
Document document4 = Jsoup.parse(webPage4);
Elements links4 = document4.select("example");
what would be the code....in JSP....
Elements is just a List of Element, so compating will look like:
for (Element element : links1) {
if(links2.contains(element)){
count++;
}
//maybe do the same thing with links3 links4.
}
If you want do it in JSP — this is another question.
I have an XML file as follows:
<rootNode>
<link>http://rootlink/</link>
<image>
<link>http://imagelink/</link>
<title>This is the title</title>
</image>
</rootNode>
The XML Java code using DOM is as follows:
NodeList rootNodeList = element.getElementsByTagName("link");
This will give me all of the "link" elements including the top level and the one inside the "image" node.
Is there a way to just get the "link" tags for rootNode within one level and not two such as is the case for the image link? That is, I just want the http://rootlink/ "link".
You could use XPath:
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
NodeList links = (NodeList) xpath.evaluate("rootNode/link", element,
XPathConstants.NODESET);
I couldn't find any methods to do that either so I wrote this helper function,
public static List<Element> getChildrenByTagName(Element parent, String name) {
List<Element> nodeList = new ArrayList<Element>();
for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
if (child.getNodeType() == Node.ELEMENT_NODE &&
name.equals(child.getNodeName())) {
nodeList.add((Element) child);
}
}
return nodeList;
}
If you can use JDOM instead, you can do this:
element.getChildren("link");
With standard Dom the closest you can get is to iterate the child nodes list (by calling getChildNodes() and checking each item(i) of the NodeList, picking out the nodes with the matching name.
I know this is an old question, but nonetheless I have an alternative solution to add.
It's not the most efficient, but works:
Get all the children using getElementsByTagName, then just check each one has the same parent to the one you started with.
I use this because I have a set of results, each result can have results nested inside it. When I call my Result constructor I need to add any nested results, but as they themselves will look for their own children I don't want to add children to the current level (their parent will add them).
Example:
NodeList children = resultNode.getElementsByTagName("result");
for(int i = 0; i<children.getLength(); i++){
// make sure not to pick up grandchildren.
if(children.item(i).getParentNode().isSameNode(resultNode)){
addChildResult(new Result((Element)children.item(i)));
}
}
Hope this helps.
I wrote this function to get the node value by tagName, restrict to top level
public static String getValue(Element item, String tagToGet, String parentTagName) {
NodeList n = item.getElementsByTagName(tagToGet);
Node nodeToGet = null;
for (int i = 0; i<n.getLength(); i++) {
if (n.item(i).getParentNode().getNodeName().equalsIgnoreCase(parentTagName)) {
nodeToGet = n.item(i);
}
}
return getElementValue(nodeToGet);
}
public final static String getElementValue(Node elem) {
Node child;
if (elem != null) {
if (elem.hasChildNodes()) {
for (child = elem.getFirstChild(); child != null; child = child
.getNextSibling()) {
if (child.getNodeType() == Node.TEXT_NODE) {
return child.getNodeValue();
}
}
}
}
return "";
}