Iterating through in LinqToXml query - c#

As I haven't work yet with LinqToXml I'd like to ask you for help
Source XML:
<Projects xmlns="">
<Project id="12345">
<Name>AName</Name>
</Project>
<Project id="23456">
<Name>BName</Name>
</Project>
</Projects>
Linq query:
var q = (from xe in datasource.Descendants()
select new Data{
ID = xe.Name.ToString(),
Name = xe.Value.ToString()
}).ToList();
Output:
Project AName
Name AName
Project BName
Name BName
Desired output:
12345 AName
23456 BName
So it seems that query is iterating through all descendants and takes Name as Node Name and Value as Node value. How should I modify it to get desired output?
How about something like this:
Get all <Name> nodes for iteration
select Value of that node and id attribute for ancestor
P.S. Do you recommend any particular tutorials for LinqToXml?

var q = (from p in datasource.Descendants("Project")
select new Data {
ID = (string)p.Attribute("id"),
Name = (string)p.Element("Name")
}).ToList();
Consider also to have ID property of integer type, then you will be able to parse it this way:
ID = (int)p.Attribute("id")
Also you can use methods (fluent) syntax:
var q = datasource
.Descendants("Project")
.Select(p => new Data {
ID = (string)p.Attribute("id"),
Name = (string)p.Element("Name") })
.ToList();

Related

Count the hits in a chained chart

I'm new in LinqPad. I already solved all my problems except the following:
How can I get the .Count() of one column, respectively the list in the one cell. Like you see in the picture.
My code example is:
var test2 = (from d in DocumentTypeLabels
select new {d.DocumentTypePIMID, d.Name, d.DocumentType.Documents}
)
.Take(1); test2.Dump();
My idea was:
var test2 = (from d in DocumentTypeLabels
select new {d.DocumentTypePIMID, d.Name, (d.DocumentType.Documents).Count()}
)
.Take(1); test2.Dump();
but unfortunately this doesn't work.
Does anyone have an idea?
Given the type of DocumentType.Documents is a Collection, you could try using the Length or Count properties from you collection and name a property on your result. For sample:
var test2 = (from d in DocumentTypeLabels
select new {
d.DocumentTypePIMID,
d.Name,
DocumentsTotal = d.DocumentType.Documents.Count()
}).Take(1);
When you do not define a name for a property on the select statement, it will generate a property with a name of the property you have provided. For sample: x.Name will be Name on result.

Way to populate class object in single LINQ XML query?

Given the following XML snippet, is there a way to both query and populate a class object in one LINQ statement? It's confusing because of the need to select using attribute values.
<data>
<array>
<item key="0">
<map>
<item key="mrid">53030</item>
<item key="mrtitle">GeneralFeedback</item>
</map>
</item>
</array>
</data>
Class:
public class Incident
{
public int ID { get; set; }
public string Title { get; set; }
}
Current (working) code (where result is the XML snippet as a string):
var data = XDocument.Parse(result);
var id = from item in data.Descendants("item")
where item.Attribute("key").Value == "mrid"
select item.Value;
var title = from item in data.Descendants("item")
where item.Attribute("key").Value == "mrtitle"
select item.Value;
var incident = new Incident
{
ID = Convert.ToInt32(id.FirstOrDefault()),
Title = title.FirstOrDefault()
};
Based on the answers given I learned some useful things and came up with this variation:
var incidents = data.Descendants("map")
.Select(i => i.Descendants("item")
.ToDictionary(m => m.Attribute("key").Value, m => m.Value))
.Where(i => i.ContainsKey("mrid")
&& i.ContainsKey("mrtitle"))
.Select(i => new Incident
{
ID = int.Parse(i["mrid"]),
Title = i["mrtitle"]
});
One thing I really like is that this creates an IEnumerable that allows for multiple incidents being present in the XML data.
is there a way to both query and populate a class object in one LINQ statement?
Yes, well sorta ... and it remains quite ugly. The below "single" multi-step LINQ statement ensures only the items that belong to the same map element get selected. Like your code sample, it will blow up in your face if the items with the required key values are missing (or the "mrid" element is not an int).
var key_vals = new List<string> { "mrid", "mrtitle" };
var xdoc = XDocument.Load(#"c:\temp\test.xml");
var incidents = xdoc.Descendants("map").Select(map => {
var items = map.Descendants("item").Where(i => key_vals.Contains(i.Attribute("key").Value));
var idItem = items.Where(x => x.Attribute("key").Value == "mrid").First();
var titleItem = items.Where(x => x.Attribute("key").Value == "mrtitle").First();
return new Incident {
ID = int.Parse(idItem.Value),
Title = titleItem.Value
};
});
foreach (var i in incidents)
Console.WriteLine("ID = {0}, Title = {1}", i.ID, i.Title);
It will produce the output below for your given xml input file:
ID = 53030, Title = GeneralFeedback
Check out this post to learn how to convert your XML schema to a C# class
Generate C# class from XML
Then you can use your new type and de-serialize your XML to a class
XmlSerializer serializer = new XmlSerializer(typeof(Incident));
using (StringReader reader = new StringReader(xmlDocumentText))
{
Incident incident= (Incident)(serializer.Deserialize(reader));
}
Alex has already given a perfect answer, but I find this a little more readable (:
The Where clause ensures each item found, has the keys required to construct an Incident.
var incidents = xdoc.Root
.Element("array")
.Elements("item")
.Select(i => i.Element("map")
.Elements("item")
.ToDictionary(m => m.Attribute("key").Value,
m => m.Value))
.Where(i => i.ContainsKey("mrid")
&& i.ContainsKey("mrtitle"))
.Select(i => new Incident
{
ID = int.Parse(i["mrid"]),
Title = i["mrtitle"]
});

Use where condition for retrieving the xml data with C#

I want to fetch the data from an xml file. I am fetching the id of node from the previous page. And on next page I want to display the data from xml of that id. I am passing id of node using query string but when I run my code its give me this error
System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.
Here is my code
XElement xid = XElement.Parse(Request.QueryString["ID"]);
// var id = Request.QueryString["ID"];
var doc = XDocument.Load(Server.MapPath("~/Data/BlogContent.xml"));
var result = doc.Descendants("post")
.Where(x => x.Element("id") == xid)
.Select(x => new
{
id = x.Element("id").Value,
title = x.Element("title").Value,
Discription = x.Element("Discription").Value,
dt = x.Element("dt").Value,
mnt = x.Element("mnt").Value,
yr = x.Element("yr").Value
}).OrderByDescending(x => x.id).Take(5);
Repeater1.DataSource = result;
Repeater1.DataBind();
Here is my xml
<?xml version="1.0" encoding="utf-8"?>
<content>
<post>
<id>1</id>
<title>fds</title>
<Discription>fdsafsdf</Discription>
<dt>21</dt>
<mnt>6</mnt>
<yr>2013</yr>
</post>
</content>
Please tell me where I am going wrong
You don't indicate the line causing the error, but I bet it's this one:
XElement xid = XElement.Parse(Request.QueryString["ID"]);
Most likely "ID" in your query string is an identifier of some sort, not XML - hence the error.
Something like this is what you want:
string xid = Request.QueryString["ID"];
Then you can use it in your where clause.
Also, there's an error in your where clause - you're trying to compare an XElement to a value - you need to get the value of the XElement using it's Value property:
Where(x => x.Element("id").Value == xid)
XElement.Value returns a string - so simply take the string value from the query string and use it in the comparison in your where clause.
Everything Put Together
string xid = Request.QueryString["ID"];
var doc = XDocument.Load(Server.MapPath("~/Data/BlogContent.xml"));
var result = doc.Descendants("post")
.Where(x => x.Element("id").Value == xid)
.Select(x => new
{
id = x.Element("id").Value,
title = x.Element("title").Value,
Discription = x.Element("Discription").Value,
dt = x.Element("dt").Value,
mnt = x.Element("mnt").Value,
yr = x.Element("yr").Value
}).OrderByDescending(x => x.id).Take(5);
Repeater1.DataSource = result;
Repeater1.DataBind();

Trying to read XML attributes via LINQ

xDoc variable loads the XML content but I am not able to retrieve any information. It comes back NULL:
var xDoc = XDocument.Load(Config.CredentialFileName);
//method 1
IEnumerable<XElement> rows = from row in xDoc.Descendants("domain")
where (string)row.Attribute("name") == "TEST"
select row;
//method 2
var list = xDoc.Descendants("domain")
.Select(d => new
{
name = d.Attribute("name").Value,
username = d.Attribute("username").Value,
password = d.Attribute("password").Value //,
})
.Where(a => a.name == "TEST")
.ToList();
XML file:
<domains>
<domain name="TEST" userName="test" password="tSEvmlsmwEkjSxUwrCVf3G6"/>
</domains>
Thank you
Your first method works just fine with xml you provided. Make sure you are parsing xml with exactly same structure. Also check that you have at least one domain element with name equal to TEST. And make sure you don't have namespaces defined in your xml.
Second method has typo in userName attribute name (you have lower case username):
var list = xDoc.Descendants("domain")
.Select(d => new {
name = d.Attribute("name").Value,
username = d.Attribute("userName").Value, // <-- typo here
password = d.Attribute("password").Value
})
.Where(a => a.name == "TEST")
.ToList();
Also, I'd recommend to use casting instead of reading node Value property, because getting this property will throw an exception if node not exist.
var domains = from d in xDoc.Descendants("domain")
let name = (string)d.Attribute("name")
where name == "TEST"
select new {
Name = name,
Username = (string)d.Attribute("userName"),
Password = (string)d.Attribute("password")
};
See below.
var xDoc= XElement.Load(Config.CredentialFileName);
var result = xDoc.Elements("domain").Where(x => x.Attribute("name").Value.Equals("TEST")).ToList();

Using a List in a where clause in Entity Framework

I am trying to retrieve document id's over a one-to-many table. I want to use a List in the where clause to find all id's that are connected with every element in the list.
List<int> docIds = (from d in doc
where _tags.Contains(d.Tags)
select d.id).ToList<int>();
I know that the contains must be incorrect but I can't work it out. If I try a foreach I can't work out how to check if the document contains all Tags.
If you want that all d.Tags should be in the included in the _tags list, you can try:
List<int> docIds = (from d in doc
where d.Tags.All(t => _tags.Contains(t))
select d.id).ToList<int>();
If you want that d.Tags should contain all the item from _tags you need:
List<int> docIds = (from d in doc
where _tags.All(t => d.Tags.Contains(t))
select d.id).ToList<int>();
But I don't know how it translates to SQL by EF so maybe you need to evaluate it on the client site.
Use a join:
List<int> docIds = (from d in doc
from t in tags
where d.Tags.Contains(t)
select d.id).ToList<int>();

Categories

Resources