Reputation:
I have the following Xml file
<?xml version="1.0" encoding="UTF-8"?>
<session xmlns="http://winscp.net/schema/session/1.0" name="test" start="2014-04-04T15:54:09.728Z">
<upload>
<filename value="D:\ftp\test1.TXT" />
<destination value="/in/test1.TXT" />
<result success="true" />
</upload>
<touch>
<filename value="/in/test1.TXT" />
<modification value="2014-03-27T12:45:20.000Z" />
<result success="true" />
<upload>
<filename value="D:\ftp\test2.TXT" />
<destination value="/in/test2.TXT" />
<result success="true" />
</upload>
<touch>
<filename value="/in/test2.TXT" />
<modification value="2014-03-27T12:45:20.000Z" />
<result success="false" />
</touch>
</session>
I need to and I'd like to browse all filename
elements where result success="true"
for both nodes touch
and upload
.
I will get only D:\ftp\test1.TXT
So this is my code :
string file =@"C:\\Program.xml";
if (File.Exists(file))
{
try
{
XElement root = XElement.Load(file);
IEnumerable<XElement> filename =
from el in root.Elements("upload")
where (string)el.Attribute("result success") == "true"
select el;
foreach (XElement el in filename)
Console.WriteLine(el);
}
}
How can I modify the code above to get my purpose?
Upvotes: 0
Views: 94
Reputation: 33381
Try this:
var doc = XDocument.Parse(file);
XNamespace ns = "http://winscp.net/schema/session/1.0";
var elements = doc.Descendants(ns + "upload")
.Where(e =>
(string)(e.Element(ns + "result")
.Attribute("success")) == "true");
UPDATE
Since requirements changed here is updated solution:
string xml = @"<?xml version='1.0' encoding='UTF-8'?>
<session xmlns='http://winscp.net/schema/session/1.0' name='test' start='2014-04-04T15:54:09.728Z'>
<upload>
<filename value='D:\ftp\test1.TXT' />
<destination value='/in/test1.TXT' />
<result success='true' />
</upload>
<touch>
<filename value='/in/test1.TXT' />
<modification value='2014-03-27T12:45:20.000Z' />
<result success='true' />
</touch>
<upload>
<filename value='D:\ftp\test2.TXT' />
<destination value='/in/test2.TXT' />
<result success='true' />
</upload>
<touch>
<filename value='/in/test2.TXT' />
<modification value='2014-03-27T12:45:20.000Z' />
<result success='false' />
</touch>
</session>";
var doc = XDocument.Parse(xml);
XNamespace ns = "http://winscp.net/schema/session/1.0";
var fileNames = doc.Descendants(ns + "result")
.Where(r => (string)r.Attribute("success") == "true")
.Select(e => (string)e.ElementsBeforeSelf(ns + "filename").Single().Attribute("value"));
Single
used for some scheme validation. In order, if there are no exact one filename
in each node touch or upload node you will get exception.
Upvotes: 0
Reputation: 70523
After the update:
void Main()
{
XElement root = XElement.Parse (
@"<?xml version='1.0' encoding='UTF-8'?>
<session name='test' start='2014-04-04T15:54:09.728Z'>
<upload>
<filename value='D:\ftp\test1.TXT' />
<destination value='/in/test1.TXT' />
<result success='true' />
</upload>
<touch>
<filename value='/in/test1.TXT' />
<modification value='2014-03-27T12:45:20.000Z' />
<result success='true' />
</touch>
<upload>
<filename value='D:\ftp\test2.TXT' />
<destination value='/in/test2.TXT' />
<result success='true' />
</upload>
<touch>
<filename value='/in/test2.TXT' />
<modification value='2014-03-27T12:45:20.000Z' />
<result success='false' />
</touch>
</session>");
var upload = from el in root.Elements("upload") select el;
var touch = from el in root.Elements("touch") select el;
// use zip to join the two lists together based on ordering to a new object
// this WON'T work if the lists are different lengths!
var filename = upload.Zip(touch,(u,t) => new { upload = u, touch = t })
.Where(item => item.upload.Descendants("result").First().Attribute("success").Value == "true"
&& item.touch.Descendants("result").First().Attribute("success").Value == "true")
.Select(item => item.upload.Descendants("filename").First().Attribute("value").Value);
foreach (string el in filename)
Console.WriteLine(el);
}
NB, I took out the namespace on the XML content to make it clearer. Feel free to put it back in. (You will have to prefix your names with the namespace if you do.)
Also, I did this in Linq because it was asked for, I think it would be faster to use a for loop over the upload and touch arrays.
Here is how you would do that:
var uploada = upload.ToArray();
var toucha = touch.ToArray();
List<string> filename = new List<string>();
for(int index = 0; index < uploada.Length ; index++)
{
if (uploada[index].Descendants("result").First().Attribute("success").Value == "true"
&& toucha[index].Descendants("result").First().Attribute("success").Value == "true")
filename.Add(uploada[index].Descendants("filename").First().Attribute("value").Value);
}
This worked for me:
void Main()
{
XElement root = XElement.Parse (
@"<?xml version=""1.0"" encoding=""UTF-8""?>
<session name=""test"" start=""2014-04-04T15:54:09.728Z"">
<upload>
<filename value=""D:\ftp\test1.TXT"" />
<destination value=""/in/test1.TXT"" />
<result success=""true"" />
</upload>
<touch>
<filename value=""/in/test2.TXT"" />
<modification value=""2014-03-27T12:45:20.000Z"" />
<result success=""true"" />
</touch>
</session>");
var filename = from el in root.Elements("upload")
where el.Descendants("result").First().Attribute("success").Value == "true"
select el.Descendants("filename").First().Attribute("value").Value;
Console.WriteLine(filename);
}
NB, I took out the namespace on the XML content to make it clearer. Feel free to put it back in. (You will have to prefix your names with the namespace if you do.)
Upvotes: 1
Reputation: 59645
I would probably use XPath because it yields cleaner code.
var filenamesXPath = "/session/*[result[@success='true']]/filename";
var filenames = document.XPathSelectElements(filenamesXPath);
But this will not just work because of the missing namespace handling - what you actually need is the following.
var document = XDocument.Load(file);
var namespaces = new XmlNamespaceManager(new NameTable());
namespaces.AddNamespace("ns", document.Root.GetDefaultNamespace().NamespaceName);
var filenamesXPath = "/ns:session/*[ns:result[@success='true']]/ns:filename";
var filenames = document.XPathSelectElements(filenamesXPath, namespaces);
Upvotes: 0
Reputation: 786
var xe = XElement.Parse(xml);
var ns = xe.Name.Namespace;
var filenames = from d in xe.Elements()
let success = d.Element(ns + "result").Attribute("success")
where success != null & success.Value == "true"
select d.Element(ns + "filename").Attribute("value").Value;
filenames.Dump();
This should return you a list of filenames that have a result "true".
Upvotes: 0