replacing value only from an Xelement using linq - c#

EDIT: A bit more detailed HTML document... In short- how do I actually do the lookup and where precisely should the element.setvalue or element.value appear in the query...
Edit 2: The list of monkey id does not appear clear so I will add proper id's and add additional properties to my Lookup data object, sorry for the confusion! The reason I have used a list is bacause my datasource could be from anywhere also I have used a List object because I do not really know the proper usage of Dictionary (I am a newbie to coding hence why my question is all over the place, please bear with me)
I have an XElement which is a properly formatted HTML document, I am trying to replace only the value of a html element with a value contained in a List Object for example
<div id="pageContainer">
<p> some guy wants to <b>buy</b> a <h4><label id="monkey23">monkeyfield</label></h4> for some price that I do not have a clue about, maybe we should <i>suggest</i> a list of other monkeys he may like:
</p>
<h3>list of special monekeys you may want chappy...</h3>
<br />
<ul>
<li><label id="monkey13">monkeyfield</label></li>
<li><label id="monkey3">monkeyfield</label></li>
<li><label id="animal4">animalfield</label></li>
<li><label id="seacreature5">seacreaturefield</label></li>
<li><label id="mamal1">mamal field</label></li>
</ul>
</div>
Note: the value "monkeyfield" is a temporary value inserted onscreen for the purpose of identifying this is a field, once the values from the data source is binded the new values should appear.
public class LookupData
{
public string id{get;set;}
public string value{get;set;}
public string Type{get;set;}
public string Url{get;set;}
}
...
public void DataTransformerMethod()
{
var data = new List<LookupData>();
data.add(new LookupData{id="monkey3", value="special monkey from africa" });
data.add(new LookupData{id="monkey13", value="old monkey from china" });
data.add(new LookupData{id="seacreature5", value="sea monkey" });
data.add(new LookupData{id="animal4", value="rhino" });
data.add(new LookupData{id="mamal1", value="some mamal creature" });
//what linq query will iterate over the document and set the values from the values
//found in the list?
var answer = from x in HtmlDocAsAXelement
where x.Attributes()
.Any(a=> data.AsEnumerable().Where(f=> f.Name == a.Name) );
//somehow I should use .SetValue(a.value)???
SaveTheNewXElement(answer ); //all other original data must stay in tact...
}

Well, you need to iterate over all the XElements which need changing - and set their value by just calling the Value setter:
element.Value = "newvalue";
It would be trickier if the element had multiple text nodes and you only wanted to change one of them, but as there's no other content within the element, this should be fine for you.
EDIT: After the discussion, I would do something like this:
Dictionary<string, string> replacements = data.ToDictionary(x => x.id,
x => x.value);
foreach (XElement element in HtmlDocAsAXelement.Descendants())
{
string newValue;
string id = (string) element.Attribute("id");
if (id != null && replacements.TryGetValue(id, out newValue))
{
element.Value = newValue;
}
}

You can't do that "easily", because there is no foreach equivalent in LINQ. That's on purpose. See http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx and LINQ equivalent of foreach for IEnumerable<T>.
I would suggest you just do a normal foreach over the query results.

Related

How to collect a Value out of multiple div elements in a list with Selenium in C#?

i have a list on a website that stores the part number and the order number.
in this list are different div elements and i would like to export every part number in this list.
The list looks like this:
<div class="spareValue">
<span class="label">OrderNumber:</span>
<span class="PartNumber">180011</span>
</div>
<div class="spareValue">
<span class="label">SparePartNumber:</span>
<span class="PartNumber">01002523</span>
</div>
How can i export every OrderNumber and put them into a list in c# that i can work with the values??
lot of ways to do that:
var spans = driver.FindElements(By.CssSelector("div.spareValue span"));
var nbrspans = spans.Count;
var Listordernumber = new List<string>();
for(int i = 0; i < nbrspans; i +=2)
{
if (spans[i].GetAttribute("textContent") != "OrderNumber:") continue;
Listordernumber.Add(spans[i + 1].GetAttribute("textContent"));
}
so Listordernumber contains the result
if you prefer linq, you could use that:
string path = "//div[#class='spareValue' and ./span[text()='OrderNumber:']]/span[#class = 'PartNumber']";
var Listordernumber = driver.FindElements(By.XPath(path)).Select(s => s.GetAttribute("textContent")).ToList();
Oke, you want every partnummer, that belongs to an ordernummer. That leads me to this xPath, find the div that has a ordernumber in it, than find the partnumber element inside.
Then to find them all (or none).
Last put them all in a neat list, selecting the partnumber text:
string path = "//div[#class='spareValue' and ./span[text()='OrderNumber:']]/div[#class='PartNumber']"
var elements = driver.findElements(By.XPath(path));
var listOrderNumber = elements.Select(e=>e.Text);
var els = driver.FindElements(By.XPath("//*[text()='OrderNumber:']"));
foreach(var el in els){
var el = els.FindElement(By.XPath("./../span[#class='PartNumber']"));
console.writeline("OrderNumber: "+el.Text());
}
first, you have to find all elements that have "OrderNumber:" text on it and eliminate all elements that don't.
now, iterate through all elements that have "OrderNumber:" we have found from step above and go to its parent node, then find all element inside the parent node that the class name is "PartNumber".

How do you Request[""] with a Dynamic Variable? Request["#Variable"]?

I'm building a form, where the number of questions, or inputs on the form varies depending on the value in a database.Each input on the form is a radio type. The name of the tags are dynamic and are loaded from the database using #db.row.questionID which would look something like: <span name=#id> and equal a value of 1 through whatever queries were requested.
My issue is, i wrote the form using post, and i want to submit the values back into a separate database, but i dont know how to request multiple values, that changes dynamically based on query.
Sample code i wrote, it doesnt give me any errors, but it doesnt give me any results either.
foreach(var prow in poll){
var Question = prow.PollId;
if (Request.Form["#prow.PollId"] == "A") {
int AnsA = row.ResultsA;
AnsA = AnsA + 1;
db.Execute("UPDATE Results SET ResultsA=#0 WHERE ResultsId=#1", AnsA, Question);
}
i have also tried:
if (Request["prow.PollId"] == "B") {
int AnsB = row.ResultsB;
AnsB += 1;
db.Execute("UPDATE Results SET ResultsB=#0 WHERE ResultsId=#1", AnsB, prow.PollId);
}
Do you want to get value in form with dynamic inputs? If yes, you can try this:
NameValueCollection nvc = Request.Form;
foreach (var item in Request.Form.AllKeys)
{
//do something you want.
// Examble : if(item == "A")// item will return name of input
// Note: nvc[item] return value of input
}
Update:
Request.Form.AllKeys will return all of input name in form.
We use foreach to lopp through colections of input name.
Use nvc[item] or Request.Form[item] to get value of input.
You can read this article :c#: get values posted from a form

Trimming value and text from SelectItemList using Linq

I am trying to trim the value/text combos of a IEnumerable<SelectListItem> item in C#. I am able to do this with the code below but was wondering if it could be accomplished with Linq?
IEnumerable<SelectListItem> list = //function that fills the IEnumerable<SelectListItem>;
foreach (SelectListItem item in list)
{
item.Value = item.Value.Trim();
item.Text = item.Text.Trim();
}
LINQ is designed to define queries. What you have there is not a query; it is modifying/mutating items. As such, LINQ is not an effective tool to accomplish that, a foreach loop is (your implementation of one is just fine). If you wanted to use LINQ it would be to create entirely new items, rather than modifying existing items. That may or may not be what you really wanted to do. If it is, then it would be:
list.Select(item => new SelectListItem()
{
Value = item.Value.Trim(),
Text = item.Text.Trim(),
});
var result = list.Select(x => new SelectListItem {
Value = x.Value.Trim(),
Text = x.Text.Trim()
});
I believe this should do the trick. Why didn't yours work, by the way?

Select specific nodes in XML with LINQ

I'm writing a function that loads and XML document and converts it to a CSV. Since I need only some values from the XML file, the goal i'm trying to achieve is to select only the nodes I'm interested in.
Here's my code:
XDocument csvDocument = XDocument.Load(tempOutput);
StringBuilder csvBuilder = new StringBuilder(1000);
foreach (XElement node in csvDocument.Descendants("Sample"))
{
foreach (XElement innerNode in node.Elements())
{
csvBuilder.AppendFormat("{0},", innerNode.Value);
}
csvBuilder.Remove(csvBuilder.Length -1, 1);
csvBuilder.AppendLine();
}
csvOut = csvBuilder.ToString();
But, in this way I'm selectin ALL the child nodes inside the "Sample" node.
In the XML, "Sample" tree is:
<Sample Type="Object" Class ="Sample">
<ID>1</ID>
<Name>10096</Name>
<Type>2</Type>
<Rep>0</Rep>
<Selected>True</Selected>
<Position>1</Position>
<Pattern>0</Pattern>
</Sample>
Code works flawlessly, but I need only "ID" and "Selected" to be selected and their values written inside the CSV file.
Could anyone point me in the right direction, please?
Thanks.
Learn more about Linq-to-xml here. You're not really taking advantage of the 'linq-edness' of XObjects
var samples = csvDocument.Descendants("Sample")
.Select(el => new {
Id = el.Element("ID").Value,
Selected = el.Elemnt("Selected").Value
});
This creates for you an IEnumerable<T> where 'T' is an anonymous type with the properties Id and Selected.
You can parse (int.Parse or bool.Parse) the Id and Selected values for type safety. But since you are simply writing to a StringBuilder object you may not care ...just an FYI.
The StringBuilder object can then be written as follows:
foreach (var sample in samples) {
csvBuilder.AppendFormat(myFormattedString, sample.Id, sample.Selected);
}
The caveat to this is that your anonymous object and the for-each loop should be within the same scope. But there are ways around that if necessary.
As always, there is more than one way to skin a cat.
Update ...in ref. to comment:
foreach (XElement node in csvDocument.Descendants("Sample"))
{
foreach (XElement innerNode in node.Elements())
{
// this logic assumes different formatting for values
// otherwise, change if statement to || each comparison
if(innerNode.Name == "ID") {
// append/format stringBuilder
continue;
}
if(innerNode.Name == "Selected") {
// append/format stringBuilder
continue;
}
}
csvBuilder.Remove(csvBuilder.Length -1, 1);
csvBuilder.AppendLine();
}

Getting only the DIRECT InnerText of an IHTMLElement

Consider the following html code:
<div id='x'><div id='y'>Y content</div>X content</div>
I'd like to extract only the content of 'x'. However, its innerText property includes the content of 'y' as well. I tried iterating over its children and all properties but they only return the inner tags.
How can I access through the IHTMLElement interface only the actual data of 'x'?
Thanks
Use something like:
function getText(this) {
var txt = this.innerHTML;
txt.replace(/<(.)*>/g, "");
return txt;
}
Since this.innerHTML returns
<div id='y'>Y content</div>X content
the function getText would return
X content
Maybe this'll help.
Use the childNodes collection to return child elements and textnodes
You need to QI IHTMLDomNote from IHTMLelement for that.
Here is the final code as suggested by Sheng (just a part of the sample, of course):
mshtml.IHTMLElementCollection c = ((mshtml.HTMLDocumentClass)(wbBrowser.Document)).getElementsByTagName("div");
foreach (IHTMLElement div in c)
{
if (div.className == "lyricbox")
{
IHTMLDOMNode divNode = (IHTMLDOMNode)div;
IHTMLDOMChildrenCollection children = (IHTMLDOMChildrenCollection)divNode.childNodes;
foreach (IHTMLDOMNode child in children)
{
Console.WriteLine(child.nodeValue);
}
}
}
Since innerText() doesn't work with ie, there is no real way i guess.
Maybe try server-side solving the issue by creating content the following way:
<div id='x'><div id='y'>Y content</div>X content</div>
<div id='x-plain'>_plain X content_</div>
"Plain X content" represents your c# generated content for the element.
Now you gain access to the element by refering to getObject('x-plan').innerHTML().

Categories

Resources