I've a xml file like:
<starting>
<start>
<site>mushfiq.com</site>
<site>mee.con</site>
<site>ttttt.co</site>
<site>jkjhkhjkh</site>
<site>jhkhjkjhkhjkhjkjhkh</site>
<site>dasdasdasdasdasdas</site>
</start>
</starting>
Now I need to delete any <site>...</site> and value will randomly be given from a textbox.
Here is my code :
XDocument doc = XDocument.Load(#"AddedSites.xml");
var deleteQuery = from r in doc.Descendants("start") where r.Element("site").Value == txt.Text.Trim() select r;
foreach (var qry in deleteQuery)
{
qry.Element("site").Remove();
}
doc.Save(#"AddedSites.xml");
If I put the value of first element in the textbox then it can delete it, but if I put any value of element except the first element's value it could not able to delete! I need I'll put any value of any element...as it can be 2nd element or 3rd or 4th and so on.... can anyone help me out?
EDIT: Okay, with further editing, it's clearer what you want to do, and as it happens it's significantly easier than you're making it, thanks to the Remove extension method on IEnumerable<T> where T : XNode:
string target = txt.Text.Trim();
doc.Descendants("start")
.Elements("site")
.Where(x => x.Value == target)
.Remove();
That's all you need.
Related
What I'm doing is finding a specific value within an XML document and then I want to iterate upwards through each parent until it finds the parent with a specific tag name.
List<XElement> fieldReferences = new List<XElement>();
fieldReferences.AddRange(element.XPathSelectElements(string.Format("descendant::nameValue[#idref='{0}']", fieldName)));
fieldReferences.AddRange(element.XPathSelectElements(string.Format("descendant::nameValue[#value='{0}']", fieldName)));
string parentIterator = ".Parent";
string attributeValue = ".Attribute('id').Value";
string parentElementName = ".Name";
foreach (var value in fieldReferences)
{
var parentField = string.Format("{0}{1}", parentIterator, parentElementName);
while (value + parentField != "private" || value + parentField != "public")
{
// keep appending .Parent until it finds what it needs
}
//var parentField = value.Parent.Parent.Parent.Parent.Attribute("id").Value;
outputFields.Add(parentField, name.FirstOrDefault());
}
The issue that I'm having is that parentField will always be evaluated as a string so it'll never actually check the .Parent.Name property of value.
I don't work often with C# so I'm sure there's a much easier way to do this so my question is: How can I get my parentField string to evaluate the way I want OR how can I do this in a different way to achieve the same end result?
EDIT: Here's what my xml looks like. The XPAthSelectElement gets the nameValue element and I want to iterate through each parent element until I find the private tag
<private id="I need to iterate upwards through each parent element until I find this one">
<value>
<request>
<nameValues>
<nameValue idref="I found this" />
<nameValue value=""/>
</nameValues>
</request>
</value>
</private>
So you don't actually need to do this many string operations to then go crazy with XPath. Once you found your child target element, you can just use the Parent property on the XElement iteratively until you find the XElement with a private/public tag. So that gives us this code:
foreach (XElement element in fieldReferences)
{
XElement currentElement = element;
while (currentElement.Parent != null)
{
currentElement = currentElement.Parent;
if (currentElement.Name == "private" || currentElement.Name == "public") {
outputFields.Add(currentElement, /* not sure what you want here */);
break;
}
}
}
So currentElement would start out as the element with the nameValue tag from your example. In the while loop, each iteration currentElement changes to its parent node until there is no more parent or currentElement has become a private or a public tag. If the latter is the case, it gets appended to your result.
You can use the XElement.Ancestors function to get a list of all the elements that contain the nodes you found, then just select the ones you want using LINQ. No need for any loops.
var results = fieldReferences.Select(element => element.Ancestors()
.Where(ancestor => ancestor.Name == "public" ||
ancestor.Name == "private")
.FirstOrDefault());
Note that this will go all the way up the tree, and may have issues if there are multiple matching ancestors (or no matching ancestor). Let me know if that is a problem for you, and what result you want in that case, and I can make adjustments.
I am storing some settings in a settings.xml file for my C# Windows Forms Application and in that XML file I am storing e-mail addresses.
I would ultimately like to achieve being able to loop through these e-mail addresses and send one e-mail to all of them.
What would be the best way of looping through them and adding them using the To.Add method of the MailMessage class in C#?
I already have the following code below to retrieve them from the XML file:
var doc = XDocument.Load(Application.StartupPath + "//settings.xml");
StringBuilder result = new StringBuilder();
foreach (XElement c in doc.Descendants("EmailAddresses"))
{
MessageBox.Show("Results: " + c.Value, "Test");
}
I have not been able to figure out how to split the results. The results in the MessageBox are like so: "email#domain.comemail#domain.comemail#domain.com" and so on..or even if this is the best way to achieve what I want.
Your help is greatly appreciated!
Rather than evaluating the entire content below the "EmailAddresses" element, you should be enumerating its child nodes individually. Assuming the "Email<#>" elements are the only children, code similar to what commenter stribizhev offered should work fine:
foreach(XElement c in doc.Descendants("EmailAddresses")
.SelectMany(x => x.DescendantNodes()
.Where(x => x.NodeType == System.Xml.XmlNodeType.Text)))
{
MessageBox.Show("Results: " + c.Value, "Test");
}
Note that you can't actually call DescendantNodes() on the result of the call to Descendants(), as that return value is an instance of IEnumerable<XElement>, not a single XElement. But you can use the SelectMany() method to flatten the enumeration of descendants into an enumeration of their descendants.
Alternatively, you could check the node's name:
foreach(XElement c in doc.Descendants("EmailAddresses")
.SelectMany(x => x.Elements().Where(x => x.Name.StartsWith("Email")))
{
MessageBox.Show("Results: " + c.Value, "Test");
}
Based on the information you've provided so far, I would expect either of those to work fine.
The above just displays the values in the MessageBox, as in your original example. Obviously, you can just pass c.Value to the MailAddressCollection.Add() method instead, to add them as you wanted.
I am trying to read a file and process using LINQ.
I have a exclude list where if i encounter certain words in the file, i should omit that line
my code is
string sCodeFile = #"C:\temp\allcode.lst";
List<string> sIgnoreList = new List<string>() { "foo.c", "foo1.c" };
var wordsPerLine = from line in File.ReadAllLines(sCodeFile)
let items = line.Split('\n')
where !line.Contains(sIgnoreList.ToString())
select line;
foreach (var item in wordsPerLine)
{
console.WriteLine(item);
}
My LST file looks like below
\voodoo\foo.c
\voodoo\voodoo.h
\voodoo\std.c
\voodoo\foo1.h
in the end i want only
\voodoo\voodoo.h
\voodoo\std.c
How can i process the ignored list in contains? with my above code i dont get the desired output for sure
can any one help?
regards,
Karthik
Revised my answer. The bug is that you're doing a ToString on the ignore list, which certainly will not work. You must check each item in the list, which can be done using something like this:
where !sIgnoreList.Any(ignore => line.Contains(ignore))
A curiosity: since the above lambda is just passing a value into a method that only take the value as a parameter, you can write this even more compact as a method group like this:
where !sIgnoreList.Any(line.Contains)
Try this.
string sCodeFile = #"C:\temp\allcode.lst";
List<string> sIgnoreList = new List<string>() { "foo.c", "foo1.c" };
var wordsPerLine = File.ReadAllLines(sCodeFile).Where(n =>
{
foreach (var ign in sIgnoreList)
{
if (n.IndexOf(ign) != -1)
return false;
}
return true;
});
It passes the current element (n) to a lambda function, which checks it against every element of the sIgnoreList. Returning false means the element is ignored, true means it's returned.
Change it to:
where !sIgnoreList.Contains(line)
You need to compare each single line and check that it doesn't exist in the ignore list.
That's why the Vladislav's answer did not work.
Here's the working solution:
var result = from line in File.ReadAllLines(codeFile)
where !ignoreList.Any(line.Contains)
select line;
The problem was you didn't want to check for the whole path and messed up words/lines part a bit.
I nominate me for village idiot.
Why doesn't this work:
foreach (XElement clientField in _clientXml.Descendants("row").Descendants())
{
var newFieldName =
from sourceField in _sourceEntries.Descendants("Field")
where (string)sourceField.Attribute("n") == (string)clientField.Attribute("n")
select new
{
FieldName = ((string) sourceField.Attribute("n")),
AcordRef = ((string) sourceField.Attribute("m"))
};
foreach (var element in newFieldName)
{
Console.WriteLine("Field Name: {0}",
element.FieldName, element.AcordRef);
}
}
My source XML files are loaded with XElement.Load(myFileName). In debug, clientField has an attribute n="Policy Number". The first element of _sourceEntries.Descendants("Field") also has an attribute n="Policy Number". Indeed, each element in _clientXml.Descendants("row").Descendants() has a matching row in _sourceEntries.Descendants("Field"). And, I know just enough to know that the select is lazy, so in debug I look at the Console.WriteLine block. No matter what I've tried, newFieldName is an empty set.
Just in case, here's the first element of the client file:
<Column_0 n="Policy Number">ABC000123</Column_0>
And, here's the fist element of the _sourceEntries collection:
<Field n="Policy Number" c="1" l="" s="" cd="" m="1805" f="" />
I know it's going to be something simple, but I just don't see what I'm doing wrong.
Thanks.
Randy
This accomplished what I ultimately needed to do:
foreach (var clientField in _clientXml.Descendants("row").Descendants())
{
foreach (var acordMapRef in
from sourceEntry in _clientTemplate.Descendants("SourceEntries").Descendants("Field")
where (string) clientField.Attribute("n") == (string) sourceEntry.Attribute("n")
from acordMapRef in _clientTemplate.Descendants("Acord").Descendants("Field")
where (string) sourceEntry.Attribute("m") == (string) acordMapRef.Attribute("id")
select acordMapRef)
{
clientField.Attribute("n").Value = (string) acordMapRef.Attribute("n");
}
}
But, it's surely a candidate for ugliest code of the month. One thing I noticed in fooling around is that elements in an XElement tree don't seem to match to XElements in an IEnumerable collection. You might notice in the original code, above, I had an object _sourceEntries. This was a collection derived from _clientTemplate.Descendants("SourcEntries").Descendants("Field"). I would have thought that the two forms were essentially equivalent for my purposes, but apparently not. I'd appreciate somebody commenting on this issue.
Thanks folks!
Try changing:
where (string)sourceField.Attribute("n") == (string)clientField.Attribute("n")
To:
where sourceField.Attribute("n").Value == clientField.Attribute("n").Value
how can I query an xml file where I have multiple items with the same name, so that I can get all items back. Currently I only get the first result back.
I managed to get it to work with the following code, but this returns all items where the specific search criteria is met.
What I want as output is to get two results back where the location is Dublin for example.
The question is how can I achieve this with linq to xml
Cheers Chris,
Here is the code
string location = "Oslo";
var training = (from item in doc.Descendants("item")
where item.Value.Contains(location)
select new
{
event = item.Element("event").Value,
event_location = item.Element("location").Value
}).ToList();
The xml file looks like this
<training>
<item>
<event>C# Training</event>
<location>Prague</location>
<location>Oslo</location>
<location>Amsterdam</location>
<location>Athens</location>
<location>Dublin</location>
<location>Helsinki</location>
</item>
<item>
<event>LINQ Training</event>
<location>Bucharest</location>
<location>Oslo</location>
<location>Amsterdam</location>
<location>Helsinki</location>
<location>Brussels</location>
<location>Dublin</location>
</item>
</training>
You're using item.Element("location") which returns the first location element under the item. That's not necessarily the location you were looking for!
I suspect you actually want something more like:
string location = "Oslo";
var training = from loc in doc.Descendants("location")
where loc.Value == location
select new
{
event = loc.Parent.Element("event").Value,
event_location = loc.Value
};
But then again, what value does event_location then provide, given that it's always going to be the location you've passed into the query?
If this isn't what you want, please give more details - your question is slightly hard to understand at the moment. Details of what your current code gives and what you want it to give would be helpful - as well as what you mean by "name" (in that it looks like you actually mean "value").
EDIT: Okay, so it sounds like you want:
string location = "Oslo";
var training = from loc in doc.Descendants("location")
where loc.Value == location
select new
{
event = loc.Parent.Element("event").Value,
event_locations = loc.Parent.Elements("location")
.Select(e => e.Value)
};
event_locations will now be a sequence of strings. You can get the output you want with:
for (var entry in training)
{
Console.WriteLine("Event: {0}; Locations: {1}",
entry.event,
string.Join(", ", entry.event_locations.ToArray());
}
Give that a try and see if it's what you want...
This might not be the most efficient way of doing it, but this query works:
var training = (from item in root.Descendants("item")
where item.Value.Contains(location)
select new
{
name = item.Element("event").Value,
location = (from node in item.Descendants("location")
where node.Value.Equals(location)
select node.Value).FirstOrDefault(),
}).ToList();
(Note that the code wouldn't compile if the property name was event, so I changed it to name.)
I believe the problem with your code was that the location node retrieved when creating the anonymous type didn't search for the node with the desired value.