Lts say i have XElement object doc:
<parameters mode="solve">
<inputs>
<a>value_a</a>
...
...
how do i get the value of the attribute of the first element (parameters), in other words how do i check which mode is it on.
if i write
if ((string)doc.Element("parameters").Attribute("mode").Value == "solve") { mode = 1; }
it gives me null object reference error
If doc is an XElement, as you say in your question, then you probably don't need to match it again:
if (doc.Attribute("mode").Value.ToString() == "solve") {
mode = 1;
}
If it is an XDocument, then you can use its Root property to refer to the document element:
if (doc.Root.Attribute("mode").Value.ToString() == "solve") {
mode = 1;
}
When you are calling doc.Element("parameters"), you are trying to look at the elements below the root element (in this case, the elements at the same level as <inputs>). You want to do this instead:
if (input.Attribute("mode").Value == "solve") { mode = 1; }
Just use the Root
if (doc.Root.Attribute("mode").Value.Equals("solve"))
{
mode = 1;
}
Related
My logic goes as follows: I want to find the first element that misses a given attribute, add the attribute and then find the next element which misses the element, add it and so fourth.
I find the first element missing the amount attribute in the following way:
private XmlNode GetFirstElementWithoutAmount()
{
string productXPathQuery = "//XML/Products";
XmlNodeList productList = ParentXmlDocument.SelectNodes(productXPathQuery);
foreach (XmlNode element in productList)
{
string passengerXPathQuery = "//XML/Products[ID=" + element.FirstChild.InnerText + "]/Amount";
var amount = element.SelectSingleNode(passengerXPathQuery);
if (amount == null)
{
return element;
}
}
return null;
}
When I've found the first element missing the attribute, the amount is added in the following way:
private XmlNode GetOrCreateChildXMLNode(string NewNodeName, XmlNode ParentXMLNode)
{
if (ParentXMLNode == null)
{
return null;
}
XmlNode NewXMLNode = ParentXMLNode.SelectSingleNode("//" + NewNodeName);
if (NewXMLNode == null)
{
NewXMLNode = ParentXmlDocument.CreateNode(XmlNodeType.Element, NewNodeName, string.Empty);
ParentXMLNode.AppendChild(NewXMLNode);
}
return NewXMLNode;
}
The problems is, that it only adds to the first element, and then the first function always returns the second element, even though there's more elements to come? Any ideas why this is?
You are already inside //XML/Products during your foreach loop. Point directly to subnode.
string passengerXPathQuery = "./Amount";
Running into an issue with a function I wrote for a selenuim testcase... When I run a jquery on a web element ID (#AmountToggle), it displays all the attributes. I want to verify this one ("lastChild"):
but when I run this code:
driver.FindElement(By.CssSelector("#AmountToggle")).GetAttribute("lastChild")
its returning null?!
Why is this and how can I get the correct value of this attribute?
So it looks like "lastChild" is a property, not an attribute. Although I cannot assert that for sure with what you have above.
The difference being, an attribute would appear as something in the direct html, such as:
Link
Where href and id are attributes. If lastChild doesn't appear in the html like the above examples, it won't be considered an attribute.
First try comparing these two in the javascript console:
$("#AmountToggle").attr("lastChild")
$("#AmountToggle").prop("lastChild")
The following is a workaround when you have issues with Selenium finding things. This logic will allow you to find things inside of iframes with ease, and also will allow you to use pseudo-selectors to find elements:
public static string GetFullyQualifiedXPathToElement(string cssSelector, bool isFullJQuery = false, bool noWarn = false)
{
if (cssSelector.Contains("$(") && !isFullJQuery) {
isFullJQuery = true;
}
string finder_method = #"
function getPathTo(element) {
if(typeof element == 'undefined') return '';
if (element.tagName == 'HTML')
return '/HTML[1]';
if (element===document.body)
return '/HTML[1]/BODY[1]';
var ix= 0;
var siblings = element.parentNode.childNodes;
for (var i= 0; i< siblings.length; i++) {
var sibling= siblings[i];
if (sibling===element)
return getPathTo(element.parentNode)+'/'+element.tagName+'['+(ix+1)+']';
if (sibling.nodeType===1 && sibling.tagName===element.tagName)
ix++;
}
}
";
if(isFullJQuery) {
cssSelector = cssSelector.TrimEnd(';');
}
string executable = isFullJQuery ? string.Format("{0} return getPathTo({1}[0]);", finder_method, cssSelector) : string.Format("{0} return getPathTo($('{1}')[0]);", finder_method, cssSelector.Replace("'", "\""));
string xpath = string.Empty;
try {
xpath = BaseTest.Driver.ExecuteJavaScript<string>(executable);
} catch (Exception e) {
if (!noWarn) {
//Warn about failure with custom message.
}
}
if (!noWarn && string.IsNullOrEmpty(xpath)) {
//Warn about failure with custom message.
//string.Format("Supplied cssSelector did not point to an element. Selector is \"{0}\".", cssSelector);
}
return xpath;
}
This method uses Jquery, which has more extensive search options using CssSelectors (such as pseudo selectors), and finds things 100% of the time given a good search query. This method uses JQuery to find the element, and then generates an explicit XPath to that element in the DOM, returning that XPath. With the explicit XPath, you can then tell Selenium to find the element using XPath.
It looks like the value of last-child is an element itself. If that is true, here is how you might use this in your example:
driver.FindElement(By.XPath(GetFullyQualifiedXPathToElement("$(#AmountToggle).prop('lastChild')[0]", true)));
Note three things here. The first is that I used "prop" in JQuery. Change that to "attr" if that was the correct call. Also, note the [0] index. This will return the JQuery element value as a regular javascript DOM element, which is what the method above uses. The final thing to note is the cssSelector value passed in. You can pass in just a selector to this method, such as "#SomeElementId > div", or you can pass in full JQuery, such as "$('#SomeElementId > div')".
I have a if-else if construct where in I am setting a particular value of a variable based on the XName Element local name as follows:
public struct UserObject
{
public string rawValue;
}
var xmlElements= record.Descendants("abc")
.Select(x => x.Elements().ToArray());
foreach (XElement[] elements in xmlElements)
{
foreach (XElement element in elements)
{
UserObject userObject = new UserObject();
if (element.Name.LocalName == "b036") //"b036" is a XML element tag name
userObject.rawValue = (element.Value.Trim()); //note rawvalue is of string type
else if (element.Name.LocalName == "b037")
userObject.rawValue = (element.Value.Trim());
else if (element.Name.LocalName == "b040")
userObject.rawValue = (element.Value.Trim());
}
For the name of the userObject(rawvalue) to load I want to follow this hierarchical order :-
b036, PersonName (e.g., John Smith) if not available use b037
b037, PersonName (e.g., Smith, John) if not available use b040
b040, KeyNames, a.k.a Last Name (e.g., Smith)
If none of b036, b037 or b040 are found, I do not want to set the "rawvalue" for that record
How can I construct my if-else statements or should I use switch case statements to achieve the same?
Since you want to find the value for the highest of certain elements, do a max search. By rolling your own, you avoid having to scan or test twice.
First, create a variable to represent the precedence of the interesting names:
var hierarchy = new[] { "b040", "b037", "b036" };
Now setup some variables to remember the maximum candidate found so far:
XElement candidate = null;
int candidatePos = -1;
Finally, go through all the XMLElement values and remember the maximum found:
foreach (var element in xmlElements.SelectMany(elements => elements)) {
var pos = Array.IndexOf(hierarchy, element.Name.LocalName);
if (pos >= 0) {
if (candidate == null || pos > candidatePos) {
candidate = element;
candidatePos = pos;
}
}
}
If you found a maximum, you can work with it:
if (candidate != null) { //"b036" is a XML element tag name
UserObject userObject = new UserObject();
userObject.rawValue = (candidate.Value.Trim()); //note rawvalue is of string type
// work with userObject
}
I'm having trouble figuring out how to traverse the DOM with HTML Agility Pack.
For example let's say that I wanted to find an element with id="gbqfsa".
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(Url);
var foo = from bar in doc.DocumentNode.DescendantNodes()
where bar.Attributes["id"].Value == "gbqfsa"
select bar.InnerText;
Right now I'm doing this (above), but foo is coming out as null. What am I doing wrong?
EDIT: This is the if statement I was using. I was just testing to see if the elements InnerText equaled "Google Search."
if (foo.Equals("Google Search"))
{
HasSucceeded = 1;
MessageBox.Show(yay);
}
else
{
MessageBox.Show("kms");
}
return HasSucceeded;
What you should do is:
var foo = (from bar in doc.DocumentNode.DescendantNodes()
where bar.GetAttributeValue("id", null) == "gbqfsa"
select bar.InnerText).FirstOrDefault();
You forgot FirstOrDefault() to select the first element that satisfy the condition in where.
And I replace Attributes["id"].Value by GetAttributeValue("id", null) not to throw an exception if an element does have an id attribute.
I don't think foo is coming out as null. More likely, bar.Attributes["id"] is null for some of the elements in the tree since not all descendant nodes have an "id" property. I would recommend using the GetAttributeValue method, which will return a default value if the attribute is not found.
var foo = from bar in doc.DocumentNode.DescendantNodes()
where bar.GetAttributeValue("id", null) == "gbqfsa"
select bar.InnerText;
I'm trying to populate a treeview from an XmlDocument.
The Root of the tree is set as 'Scripts' and from the root the next level should be 'Departments' which is within the XML script. I can get data from the XML document no problem. My question is when looping through the XmlDocument and adding nodes to the root node, I want to ensure that if a department is already within the treeview then it is not added again. I should also add that each Department also has a list of scripts that need to be child nodes of the department.
My code so far is:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(scriptInformation);
TreeNode t1;
TreeNode rootNode = new TreeNode("Script View");
treeView1.Nodes.Add(rootNode);
foreach (XmlNode node in xDoc.SelectNodes("//row"))
{
t1 = new TreeNode(node["DEPARTMENT"].InnerXml);
//How to check if node already exists in treeview?
}
Thanks.
if(treeView1.Nodes.ContainsKey("DEPARTMENT")){
//...
}
EDIT: Recursive method:
bool exists = false;
foreach (TreeNode node in treeView1.Nodes) {
if (NodeExists(node, "DEPARTMENT"))
exists = true;
}
private bool NodeExists(TreeNode node, string key) {
foreach (TreeNode subNode in node.Nodes) {
if (subNode.Text == key) {
return true;
}
if (node.Nodes.Count > 0) {
NodeExists(node, key);
}
}
return false;
}
Depending upon the size of your XML file, you could consider using an associated List for fast lookup. As you add each node to the TreeView also add it to the List.
If your XML document has a set structure where 'Departments' will always be indexed at 1;
ie:
index:[0] Scripts
index:[1] Department
index:[2] Script
index:[1] Department2
index:[2] Script
Then you could encapsulate the following code into a method where 'name' is a string parameter and the return type is boolean.
foreach (TreeNode node in uxTreeView.Nodes[0].Nodes) {
if (node.Name.ToLower() == name.ToLower()) {
return true;
}
}
return false;
The idea is you would call that function each time you encounter a 'Department' node in your Xml, before creating the TreeNode.
Full example:
private bool DepartmentNodeExists(string name) {
foreach (TreeNode node in uxTreeView.Nodes[0].Nodes) {
if (node.Name.ToLower() == name.ToLower()) {
return true;
}
}
return false;
}
Lastly, the easy way:
private bool DepartmentNodeExists(string name) {
if (uxTreeView.Nodes[0].ContainsKey(name)) {
return true;
}
else {
return false;
}
}
These are all just refactored and encapsulated into their own named methods, you of course could just call:
if (uxTreeView.Nodes[0].ContainsKey(name)) {
// do not create TreeNode
}
...during your parsing of your XML. PS. These examples all assume that you have the first root node in the TreeView already created and added to the TreeView.
http://www.vbdotnetforums.com/listviews-treeviews/13278-treeview-search.html#post39625
http://forums.asp.net/t/1645725.aspx/1?Check+if+child+Node+exists+on+treeview
You can do something like this:
TreeNode parentNode = t1.Parent;
if (parentNode != null}
{
if(parentNode.Nodes.Cast<TreeNode>().ToList().Find(t => t.Text.Equals(node["DEPARTMENT"].InnerXml) == null)
{
//Add node
}
}
else
{
bool isFound = true;
if (treeView1.Nodes.Cast<TreeNode>().ToList().Find(t => t.Text.Equals(node["DEPARTMENT"].InnerXml) == null)
{
isFound = false;
}
if(!isFound)
{
//Add node
}
}
Not sure about the document structure...
Couldn't you use Linq to Xml, load the document and get the distinct row ( row = department?) and consider only those elements to create a TreeNode? It is more efficient than trying to find if a node with such a text has already been added.
ex:
var rows = ( from row in XDocument.Load(document).Root.Elements("row")
select row
).Distinct(new SampleElementComparerOnNameAttribute());
Here the EqualityComparer is made on the "name" attribute value assuming the doc structure to be
<rows><row name='dep1'><script>script1</script><script>script2</script></row><row name='dep1'><script>script3</script><script>script4</script></row></rows>
I use,
string department = node["DEPARTMENT"].InnerXml;
TreeNode node = parentNode.Nodes[department] ?? parentNode.Nodes.Add(department, department);
That line guarantees that a lookup of the value department will be done first, if not found it creates it. You have to do the double entry in Add() so it will have a key value you can do the lookup with the .Nodes[department].
It depends on the structure of your input. Since you don't show how exactly you add your subnodes I can only point you towards either the Contains or the ContainsKey method of the Nodes property, either of the treeView1 itself, or of any subnodes you add. You should use an overload of the Add method to specify a key name to simplify lookup.